com2014-template/Workshop - 6 (GA, SOM).ipynb

19 KiB

None <html> <head> </head>

Make sure you run this at the begining

In [ ]:
import os
import sys
import math
import numpy as np
import matplotlib.pyplot as plt

# Append template path to sys path
sys.path.append(os.getcwd() + "/template")
In [ ]:
from utils.load_data import load_data
from utils.visualize_tsp import plotTSP

from tsp import TSP_Bench_ONE
from tsp import TSP_Bench_PATH
from tsp import TSP_Bench_ALL

Workshop Starts Here

TSP
solutions

Get familiar with your dataset

There are problems at different levels. 3 easy, 2 medium, 1 difficult.

In [ ]:
for root, _, files in os.walk('./template/data'):
    if(files):
        for f in files:
            print(str(root) + "/" + f)
In [ ]:
ulysses16 = np.array(load_data("./template/data/easy/ulysses16.tsp"))
In [ ]:
ulysses16[:]
In [ ]:
plt.scatter(ulysses16[:, 0], ulysses16[:, 1])
for i in range(0, 16):
    plt.annotate(i, (ulysses16[i, 0], ulysses16[i, 1]+0.5))

Naive Solution: In Order

In [ ]:
simple_sequence = list(range(0, 16))
print(simple_sequence)
In [ ]:
plotTSP([simple_sequence], ulysses16, num_iters=1)

Naive Solution: Random Permutation

In [ ]:
random_permutation = np.random.permutation(16).tolist()
print(random_permutation)
In [ ]:
plotTSP([random_permutation], ulysses16, num_iters=1)

Best Solution

In [ ]:
best_ulysses16 = [0, 13, 12, 11, 6, 5, 14, 4, 10, 8, 9, 15, 2, 1, 3, 7]
plotTSP([best_ulysses16], ulysses16, num_iters=1)

Calculate Fitness (Sum of all Distances)

In [ ]:
def dist(node_0, node_1, coords):
        """
        Euclidean distance between two nodes.
        """
        coord_0, coord_1 = coords[node_0], coords[node_1]
        return math.sqrt((coord_0[0] - coord_1[0]) ** 2 + (coord_0[1] - coord_1[1]) ** 2)
In [ ]:
print("Coordinate of City 0:", ulysses16[0])
In [ ]:
print("Coordinate of City 1:", ulysses16[1])
In [ ]:
print("Distance Between", dist(0, 1, ulysses16))
In [ ]:
def fitness(solution, coords):
    N = len(coords)
    cur_fit = 0
    for i in range(len(solution)):
        cur_fit += dist(solution[i % N], solution[(i + 1) % N], coords)
    return cur_fit
In [ ]:
print ("Order Fitness:\t", fitness(simple_sequence, ulysses16))
print ("Random Fitness:\t", fitness(random_permutation, ulysses16))
print ("Best Fitness:\t", fitness(best_ulysses16, ulysses16))

Naive Random Model

In [ ]:
import math
import random
from model.base_model import Model
import numpy as np

class MyRandomModel(Model):
    def __init__(self):
        super().__init__()

    def init(self, nodes):
        """
        Put your initialization here.
        """
        super().init(nodes)

    def fit(self, max_it=1000):
        """
        Put your iteration process here.
        """
        random_solutions = []
        for i in range(0, max_it):
            solution = np.random.permutation(self.N).tolist()
            random_solutions.append(solution)
            self.fitness_list.append(self.fitness(solution))

        self.best_solution = random_solutions[self.fitness_list.index(min(self.fitness_list))]
        return self.best_solution, self.fitness_list
In [ ]:
tsp_file = './template/data/easy/ulysses16.tsp'
In [ ]:
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyRandomModel)
In [ ]:
plt.plot(fitness_list, 'o-')

Genetic Algorithm

In [ ]:
import math
import random
from model.base_model import Model
from random import randint, sample

class Gene:  # City
    def __init__(self, name, lat, lng):
        self.name = name
        self.lat = lat
        self.lng = lng

    def get_distance_to(self, dest):
        return math.sqrt( (self.lng - dest.lng) ** 2 + (self.lat - dest.lat) ** 2 )

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):
        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

class MyGAModel(Model):
    def __init__(self):
        super().__init__()
        self.iteration = 0

    def init(self, nodes):
        super().init(nodes)

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

        # 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 = self.selection(new_generation, tourn_size)
            parent_2 = self.selection(new_generation, tourn_size)
            child = self.crossover(parent_1, parent_2)
            new_generation.add(child)

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

        return new_generation

    def crossover(self, 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(self, individual, rate):
        for _ in range(len(individual.genes)):
            if random.random() < rate:
                sel_genes = sample(individual.genes, 2)
                individual.swap(sel_genes[0], sel_genes[1])

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

    def fit(self, max_it=100):
        """
        Execute simulated annealing algorithm.
        """
        pop_size = 1000
        mut_rate = 0.9
        tourn_size = 100

        genes = [Gene(num, city[0], city[1]) for num, city in enumerate(self.coords)]
        self.genes = genes

        population = Population.gen_individuals(pop_size, genes)
        

        for it in range(0, max_it):
            mut_rate = mut_rate * 0.95
            if mut_rate < 0.05:
                mut_rate = 0.05
            population = self.evolve(population, tourn_size, mut_rate)
            cost = population.get_fittest().travel_cost

            it += 1
            self.fitness_list.append(cost)
            print("[step] ", it, " [mut] ", mut_rate, " [best] ", self.fitness_list[self.fitness_list.index(min(self.fitness_list))])

        self.best_solution = [gene.name for gene in population.get_fittest().genes]

        return self.best_solution, self.fitness_list
In [ ]:
tsp_problem = "./template/data/easy/ulysses16.tsp"
In [ ]:
print("Genetic Algorithm")
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_problem, MyGAModel, timeout=300)
In [ ]:
plt.plot(fitness_list, 'o-')
In [ ]:
plotTSP([best_solution], load_data(tsp_problem), num_iters=1)
In [ ]:
print ("Best Fitness:\t", fitness(best_solution, ulysses16))
print ("Best Fitness:\t", fitness(best_ulysses16, ulysses16))

Your Smart Model

In [ ]:
import math
import random
from model.base_model import Model

class MyModel(Model):
    def __init__(self):
        super().__init__()

    def init(self, nodes):
        """
        Put your initialization here.
        """
        super().init(nodes)

        self.log("Nothing to initialize in your model now")

    def fit(self, max_it=1000):
        """
        Put your iteration process here.
        """
        self.best_solution = np.random.permutation(self.N).tolist()
        self.fitness_list.append(self.fitness(self.best_solution))

        return self.best_solution, self.fitness_list

Test your Model

In [ ]:
tsp_problem = './template/data/easy/ulysses16.tsp'
In [ ]:
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyModel)

Test All Dataset

In [ ]:
tsp_path = './template'
for root, _, files in os.walk(tsp_path + '/data'):
    if(files):
        for f in files:
            print(str(root) + "/" + f)
In [ ]:
def plot_results(best_solutions, times, title):
    fig = plt.figure()
    nodes = [len(s) for s in best_solutions]
    data = np.array([[node, time] for node, time in sorted(zip(nodes, times))])
    plt.plot(data[:, 0], data[:, 1], 'o-')
    fig.suptitle(title, fontsize=20)
In [ ]:
print("Random Search")
best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyRandomModel)
In [ ]:
plot_results(best_solutions, times, "Random Model")
In [ ]:
print("Genetic Algorithm")
best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyGAModel)
In [ ]:
plot_results(best_solutions, times, "Genetic Algorithm")
In [ ]:

</html>