You are on page 1of 19

A

MINI-PROJECT REPORT ON
“Travelling salesman problem using Genetic Algorithm”

Prepared By
Mr. Kyatanavar Prasanna Roll No:86
Miss. Lokhande Sakshi Roll No:88
Mr. Magar Viraj Roll No:92
(B.E. Computer)

SAVITRIBAIPHULE PUNE UNIVERSITY


In the academic year 2019-20

Department of Computer Engineering


Sanjivani Rural Education Society’s
SanjivaniCollege of Engineering
Kopargaon - 423 603.

[30/2020]

1
ACKNOWLEDGEMENT

First and foremost, We would like to give thanks to God Almighty for His mercy and beautiful
blessings in our life. Even in the hardest times, He had always been there to guide and comfort us
and to make sure that we never stray too far from His narrow way.

We would like to thank our family for their unconditional love and support. And being there
for us, despite all the troubles and problems that grow in life.

We express our sincere gratitude to Prof. D. B. Kshirsagar Head of Department (Computer)


SRESCOE for his unending support and encouragement during the years We have studied under
his tutelage.
We are greatly indebted to project guide Prof T.Bhaskar, Assistant Professor(Computer), for
providing valuable guidance at every stage of this project work.
Our sincere thanks go to all the teachers and staff for their help and understanding.

Kyatanavar Prasanna(86)
Lokhande Sakshi(88)
Magar Viraj(92)

2
Contents

Sr. Title Page No.


No
1 Introduction 04
2 Scope 04
3 Objective 05
4 Methodology 05
5 Requirement 08
6 Implementation 08
7 Screenshots 11
8 Conclusion 13
9 References 13

ABSTRACT:-
Travelling salesman problem (TSP) is a combinatorial optimization problem. It is NP
hard problem and TSP is the most intensively studied problem in the area of optimization. The
travelling salesman problem (TSP) is the most well-known combinatorial optimization problem.
TSP is used to find a routing of a salesman who starts from a home location, visits a
prescribed set of cities and returns to the original location in such a way that the total

3
distance travelled is minimized and each city is visited exactly once. But with the increase in
the number of cities, the complexity of the problem goes on increasing. Genetic Algorithm is
computer algorithm that search for good solutions to a problem from various possible solutions.
The genetic algorithm is a heuristic method which is used to improve the solution space for the
Travelling Salesman Problem. The genetic algorithm results in nearest optimal solution within a
reasonable time. In this system, we have solved Travelling Salesman Problem using Genetic
algorithm approach.

INTRODUCTION :-
 Travelling salesman problems are solved using genetic algorithm which solves the
problem by using the processes observed in natural evolution to solve various
optimizations and search problems. 
 Genetic algorithm improve the solution space for the Travelling Salesman Problem and
results in nearest optimal solution within a reasonable time. The basic elements of
Genetic Algorithm are chromosomes, fitness function, selection, crossover and mutation.
 Geographic coordinates of cities are provided as input to generate a edge-weighted
complete graph where the weights are the distance between the cities in kilometers.

SCOPE :-

 Travelling salesman problems is used in computational mathematics.


 Genetic algorithm improve the solution space and gives optimal solution.

OBJECTIVE:-
 To find optimal solution of travelling salesman problem within reasonable time.
 To improve solution space of travelling salesman problems.

METHODOLOGY:

Generic Python implementation of a Genetic Algorithm to solve the Travelling Salesman


Problem (TSP). The Genetic Algorithm is a Possibilistic Algorithm inspired by the Darwinean
Theory of Evolution. It searches through the space of possible solutions so as to find acceptable -
according to some criteria - solutions.
To use a Genetic Algorithm to a Problem, one must define:

4
 Population

In this problem the population is a list of strings (genomes) that each letter (allele) is a vertex of


the given graph.

Example: For a Graph with vertices V = {a,b,c,d,e}, an allele is any of the elements in V, a


genome is any permutation of the elements in V and a population is a list of N genomes.

 Fitness Function

It's the measure used to compare genomes to each other. For this problem a fitness function (and
the one we use in this implementation) is the sum of the edge's weights through the graph for a
certain path - encoded in the genome.

 Selection

Each generation must have N genomes at a time. To achieve evolution we select some at the
genomes to Crossover them and produce Offsprings that will be part of the new population. To
select them there are many techniques, two of which are:

1. Roulette Selection:

> sum_of_fitness = 0

> for each genome in population:


calculate its fitness with a Fitness Function
sum_of_fitness += genome_fitness

> for each genome in population:


probability = genome_fitness/sum_of_fitness

> choose a random genome with the calculated probabilities

2. Tournament Selection:

> choose K genomes from the population

> for each genome chosen:


calculate its fitness with a Fitness function

> choose the fittest genome

 Crossover

5
Now that we have our parent genomes from the Selection procedure, now we crossover them to
produce - potentially - fitter offsprings.

There are many options and techniques that the Crossover may be done, but the one that we use
in our implementation is the following:

> Select a subset from the first parent.

> Add that subset to the offspring.

> Any missing values are then added to the offspring from the second parent in order that they
are found.

Example:
Parent 1: a,[b, c],d, e
Parent 2: b, e, a, c, d
Offspring: e, b, c, a, d

 Mutation

After making the offsprings of the next generation, to finalize the creation of the new population
we must mutate the genomes.

To mutate a genome we just select two random alleles and swap them in a genome

Note: the mutation does not occur always. It occurs with a user-defined probability.

 Elitism

In Genetic Algorithms sometimes the fittest genomes from the current generation are passed
directly to the next, contesting with the offsprings and crossover to - potentially - fitter solutions.

This step occurs during the calculation of the Fitness function value for each genome. When all
values are computed the genome with the minimum value is appended directly to the next
generation's population.

Algorithm Convergence

The Algorithm stops if:

 All genomes in the population are the same or


 Reached the max. Generation limit

6
System Requirements:
Software Components:
Operating System : Windows 10.
Pycharm
Matplotlab

Hardware Components:
Processer : i3
RAM : 1GB
Hard Disk : 5 GB
Monitor

Implementation:
1. tsp_ga.py:
from sys import maxsizefrom sys import maxsize
from time import time
from random import random, randint, sample
from haversine import haversine

class Gene:  # City
    # keep distances from cities saved in a table to improve execution time.
    __distances_table = {}

    def __init__(self, name, lat, lng):
        self.name = name
        self.lat = lat
        self.lng = lng

    def get_distance_to(self, dest):
        origin = (self.lat, self.lng)
        dest = (dest.lat, dest.lng)

        forward_key = origin + dest
        backward_key = dest + origin

        if forward_key in Gene.__distances_table:
            return Gene.__distances_table[forward_key]

7
        if backward_key in Gene.__distances_table:
            return Gene.__distances_table[backward_key]

        dist = int(haversine(origin, dest))
        Gene.__distances_table[forward_key] = dist

        return dist

class Individual:  # Route: possible solution to TSP
    def __init__(self, genes):
        assert(len(genes) > 3)
        self.genes = genes
        self.__reset_params()

    def swap(self, gene_1, gene_2):
        self.genes[0]
        a, b = self.genes.index(gene_1), self.genes.index(gene_2)
        self.genes[b], self.genes[a] = self.genes[a], self.genes[b]
        self.__reset_params()

    def add(self, gene):
        self.genes.append(gene)
        self.__reset_params()

    @property
    def fitness(self):
        if self.__fitness == 0:
            self.__fitness = 1 / self.travel_cost  # Normalize travel cost
        return self.__fitness

    @property
    def travel_cost(self):  # Get total travelling cost
        if self.__travel_cost == 0:
            for i in range(len(self.genes)):
                origin = self.genes[i]
                if i == len(self.genes) - 1:
                    dest = self.genes[0]
                else:
                    dest = self.genes[i+1]

                self.__travel_cost += origin.get_distance_to(dest)

        return self.__travel_cost

    def __reset_params(self):

8
        self.__travel_cost = 0
        self.__fitness = 0

class Population:  # Population of individuals
    def __init__(self, individuals):
        self.individuals = individuals

    @staticmethod
    def gen_individuals(sz, genes):
        individuals = []
        for _ in range(sz):
            individuals.append(Individual(sample(genes, len(genes))))
        return Population(individuals)

    def add(self, route):
        self.individuals.append(route)

    def rmv(self, route):
        self.individuals.remove(route)

    def get_fittest(self):
        fittest = self.individuals[0]
        for route in self.individuals:
            if route.fitness > fittest.fitness:
                fittest = route

        return fittest

def evolve(pop, tourn_size, mut_rate):
    new_generation = Population([])
    pop_size = len(pop.individuals)
    elitism_num = pop_size // 2

    # Elitism
    for _ in range(elitism_num):
        fittest = pop.get_fittest()
        new_generation.add(fittest)
        pop.rmv(fittest)

    # Crossover
    for _ in range(elitism_num, pop_size):
        parent_1 = selection(new_generation, tourn_size)
        parent_2 = selection(new_generation, tourn_size)
        child = crossover(parent_1, parent_2)
        new_generation.add(child)

9
    # Mutation
    for i in range(elitism_num, pop_size):
        mutate(new_generation.individuals[i], mut_rate)

    return new_generation

def crossover(parent_1, parent_2):
    def fill_with_parent1_genes(child, parent, genes_n):
        start_at = randint(0, len(parent.genes)-genes_n-1)
        finish_at = start_at + genes_n
        for i in range(start_at, finish_at):
            child.genes[i] = parent_1.genes[i]

    def fill_with_parent2_genes(child, parent):
        j = 0
        for i in range(0, len(parent.genes)):
            if child.genes[i] == None:
                while parent.genes[j] in child.genes:
                    j += 1
                child.genes[i] = parent.genes[j]
                j += 1

    genes_n = len(parent_1.genes)
    child = Individual([None for _ in range(genes_n)])
    fill_with_parent1_genes(child, parent_1, genes_n // 2)
    fill_with_parent2_genes(child, parent_2)

    return child

def mutate(individual, rate):
    for _ in range(len(individual.genes)):
        if random() < rate:
            sel_genes = sample(individual.genes, 2)
            individual.swap(sel_genes[0], sel_genes[1])

def selection(population, competitors_n):
    return Population(sample(population.individuals, competitors_n)).get_fittest(
)

def run_ga(genes, pop_size, n_gen, tourn_size, mut_rate, verbose=1):
    population = Population.gen_individuals(pop_size, genes)
    history = {'cost': [population.get_fittest().travel_cost]}
    counter, generations, min_cost = 0, 0, maxsize

10
    if verbose:
        print("-- TSP-GA -- Initiating evolution...")

    start_time = time()
    while counter < n_gen:
        population = evolve(population, tourn_size, mut_rate)
        cost = population.get_fittest().travel_cost

        if cost < min_cost:
            counter, min_cost = 0, cost
        else:
            counter += 1

        generations += 1
        history['cost'].append(cost)

    total_time = round(time() - start_time, 6)

    if verbose:
        print("-- TSP-GA -- Evolution finished after {} generations in {} s".for
mat(generations, total_time))
        print("-- TSP-GA -- Minimum travelling cost {} KM".format(min_cost))

    history['generations'] = generations
    history['total_time'] = total_time
    history['route'] = population.get_fittest()

    return history

from time import time
from random import random, randint, sample
from haversine import haversine

class Gene:  # City
    # keep distances from cities saved in a table to improve execution time.
    __distances_table = {}

    def __init__(self, name, lat, lng):
        self.name = name
        self.lat = lat
        self.lng = lng

    def get_distance_to(self, dest):
        origin = (self.lat, self.lng)

11
        dest = (dest.lat, dest.lng)

        forward_key = origin + dest
        backward_key = dest + origin

        if forward_key in Gene.__distances_table:
            return Gene.__distances_table[forward_key]

        if backward_key in Gene.__distances_table:
            return Gene.__distances_table[backward_key]

        dist = int(haversine(origin, dest))
        Gene.__distances_table[forward_key] = dist

        return dist

class Individual:  # Route: possible solution to TSP
    def __init__(self, genes):
        assert(len(genes) > 3)
        self.genes = genes
        self.__reset_params()

    def swap(self, gene_1, gene_2):
        self.genes[0]
        a, b = self.genes.index(gene_1), self.genes.index(gene_2)
        self.genes[b], self.genes[a] = self.genes[a], self.genes[b]
        self.__reset_params()

    def add(self, gene):
        self.genes.append(gene)
        self.__reset_params()

    @property
    def fitness(self):
        if self.__fitness == 0:
            self.__fitness = 1 / self.travel_cost  # Normalize travel cost
        return self.__fitness

    @property
    def travel_cost(self):  # Get total travelling cost
        if self.__travel_cost == 0:
            for i in range(len(self.genes)):
                origin = self.genes[i]
                if i == len(self.genes) - 1:

12
                    dest = self.genes[0]
                else:
                    dest = self.genes[i+1]

                self.__travel_cost += origin.get_distance_to(dest)

        return self.__travel_cost

    def __reset_params(self):
        self.__travel_cost = 0
        self.__fitness = 0

class Population:  # Population of individuals
    def __init__(self, individuals):
        self.individuals = individuals

    @staticmethod
    def gen_individuals(sz, genes):
        individuals = []
        for _ in range(sz):
            individuals.append(Individual(sample(genes, len(genes))))
        return Population(individuals)

    def add(self, route):
        self.individuals.append(route)

    def rmv(self, route):
        self.individuals.remove(route)

    def get_fittest(self):
        fittest = self.individuals[0]
        for route in self.individuals:
            if route.fitness > fittest.fitness:
                fittest = route

        return fittest

def evolve(pop, tourn_size, mut_rate):
    new_generation = Population([])
    pop_size = len(pop.individuals)
    elitism_num = pop_size // 2

    # Elitism

13
    for _ in range(elitism_num):
        fittest = pop.get_fittest()
        new_generation.add(fittest)
        pop.rmv(fittest)

    # Crossover
    for _ in range(elitism_num, pop_size):
        parent_1 = selection(new_generation, tourn_size)
        parent_2 = selection(new_generation, tourn_size)
        child = crossover(parent_1, parent_2)
        new_generation.add(child)

    # Mutation
    for i in range(elitism_num, pop_size):
        mutate(new_generation.individuals[i], mut_rate)

    return new_generation

def crossover(parent_1, parent_2):
    def fill_with_parent1_genes(child, parent, genes_n):
        start_at = randint(0, len(parent.genes)-genes_n-1)
        finish_at = start_at + genes_n
        for i in range(start_at, finish_at):
            child.genes[i] = parent_1.genes[i]

    def fill_with_parent2_genes(child, parent):
        j = 0
        for i in range(0, len(parent.genes)):
            if child.genes[i] == None:
                while parent.genes[j] in child.genes:
                    j += 1
                child.genes[i] = parent.genes[j]
                j += 1

    genes_n = len(parent_1.genes)
    child = Individual([None for _ in range(genes_n)])
    fill_with_parent1_genes(child, parent_1, genes_n // 2)
    fill_with_parent2_genes(child, parent_2)

    return child

def mutate(individual, rate):
    for _ in range(len(individual.genes)):

14
        if random() < rate:
            sel_genes = sample(individual.genes, 2)
            individual.swap(sel_genes[0], sel_genes[1])

def selection(population, competitors_n):
    return Population(sample(population.individuals, competitors_n)).get_fittest(
)

def run_ga(genes, pop_size, n_gen, tourn_size, mut_rate, verbose=1):
    population = Population.gen_individuals(pop_size, genes)
    history = {'cost': [population.get_fittest().travel_cost]}
    counter, generations, min_cost = 0, 0, maxsize

    if verbose:
        print("-- TSP-GA -- Initiating evolution...")

    start_time = time()
    while counter < n_gen:
        population = evolve(population, tourn_size, mut_rate)
        cost = population.get_fittest().travel_cost

        if cost < min_cost:
            counter, min_cost = 0, cost
        else:
            counter += 1

        generations += 1
        history['cost'].append(cost)

    total_time = round(time() - start_time, 6)

    if verbose:
        print("-- TSP-GA -- Evolution finished after {} generations in {} s
".format(generations, total_time))
        print("-- TSP-GA -- Minimum travelling cost {} KM".format(min_cost))

    history['generations'] = generations
    history['total_time'] = total_time
    history['route'] = population.get_fittest()

    return history

2. main.py

15
import utils
import random
import argparse
import tsp_ga as ga
from datetime import datetime

def run(args):
    genes = utils.get_genes_from(args.cities_fn)

    if args.verbose:
        print("-- Running TSP-GA with {} cities --".format(len(genes)))

    history = ga.run_ga(genes, args.pop_size, args.n_gen,
                       args.tourn_size, args.mut_rate, args.verbose)

    if args.verbose:
        print("-- Drawing Route --")

    utils.plot(history['cost'], history['route'])

    if args.verbose:
        print("-- Done --")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument('-v', '--verbose', type=int, default=1)
    parser.add_argument('--pop_size', type=int, default=500, help='Population 
size')
    parser.add_argument('--tourn_size', type=int, default=50, hel
p='Tournament size')
    parser.add_argument('--mut_rate', type=float, default=0.02, hel
p='Mutation rate')
    parser.add_argument('--n_gen', type=int, default=20, help='Number of equa
l generations before stopping')
    parser.add_argument('--cities_fn', type=str, default="data/cities.csv", h
elp='Data containing the geographical coordinates of cities')

    random.seed(datetime.now())
    args = parser.parse_args()

    if args.tourn_size > args.pop_size:

16
        raise argparse.ArgumentTypeError('Tournament size cannot be bigger th
an population size.')

    run(args)

Screen Shots:

17
Conclusion:
Combining the knowledge from heuristic methods and genetic algorithms is a promising
approach for solving the TSP. Genetic algorithms appear to find good solutions for the travelling
salesman problem, however it depends very much on the way the problem is encoded and which
crossover and mutation methods are used. genetic algorithm results in nearest optimal solution
within a reasonable time.

Reference:
1. www.towordsdatascience.com
2. www.geeksforgeeks.com
3. www.w3school.com

18
19

You might also like