Search code examples
algorithmmatlabgenetic-algorithmroulette-wheel-selection

How to implement Roulette Wheel Selection and Rank Sleection on Matlab code for the Traveling Salesman Problom?


I have an assignment coding a genetic algorithm for the traveling salesman problem. I've written some code giving correct results using Tournament Selection. The problem is, I have to do Wheel and Rank and the results I get are incorrect.

Here is my code using Tournament Selection:

clc;
clear all;
close all;

nofCities = 30;
initialPopulationSize = nofCities*nofCities;
generations = nofCities*ceil(nofCities/10);

cities = floor(rand([nofCities 2])*100+1);

figure;
hold on;
scatter(cities(:,1), cities(:,2), 5, 'b','fill');
line(cities(:,1), cities(:,2));
line(cities([1 end],1), cities([1 end],2));
axis([0 110 0 110]);

population = zeros(initialPopulationSize ,nofCities);

for i=1:initialPopulationSize 
   population(i,:) = randperm(nofCities);
end

distanceMatrix = zeros(nofCities);
for i=1:nofCities
    for j=1:nofCities
        if (i==j)
            distanceMatrix(i,j)=0;
        else
            distanceMatrix(i,j) = sqrt((cities(i,1)-cities(j,1))^2+(cities(i,2)-cities(j,2))^2);
        end
    end
end

for u=1:generations     
    tourDistance = zeros(initialPopulationSize ,1);
    for i=1:initialPopulationSize 
       for j=1:length(cities)-1
           tourDistance(i) = tourDistance(i) + distanceMatrix(population(i,j),population(i,j+1));
       end
    end
    for i=1:initialPopulationSize 
        tourDistance(i) = tourDistance(i) + distanceMatrix(population(i,end),population(i,1));
    end

    min(tourDistance)

    newPopulation = zeros(initialPopulationSize,nofCities);

    for k=1:initialPopulationSize
        child = zeros(1,nofCities);
        %tournament start
        for i=1:5
           tournamentParent1(i) = ceil(rand()*initialPopulationSize);
        end
        p1 = find(tourDistance == min(tourDistance([tournamentParent1])));
        parent1 = population(p1(1), :);
        for i=1:5
           tournamentParent2(i) = ceil(rand()*initialPopulationSize);
        end
        p2 = find(tourDistance == min(tourDistance([tournamentParent2])));
        parent2 = population(p2(1), :);
        %tournament end
        %crossover
        startPos = ceil(rand()*(nofCities/2));
        endPos = ceil(rand()*(nofCities/2)+10);

        for i=1:nofCities
           if (i>startPos && i<endPos) 
               child(i) = parent1(i);
           end
        end

        for i=1:nofCities
            if (isempty(find(child==parent2(i))))
                for j=1:nofCities
                    if (child(j) == 0)
                        child(j) = parent2(i);
                        break;
                    end
                end
            end
        end

        newPopulation(k,:) = child;
    end

    %mutation
    mutationRate = 0.015;
    for i=1:initialPopulationSize
       if (rand() < mutationRate)
           pos1 = ceil(rand()*nofCities);
           pos2 = ceil(rand()*nofCities);
           mutation1 = newPopulation(i,pos1);
           mutation2 = newPopulation(i,pos2);
           newPopulation(i,pos1) = mutation2;
           newPopulation(i,pos2) = mutation1;           
       end
    end

    population = newPopulation;
    u
end

figure;
hold on;
scatter(cities(:,1), cities(:,2), 5, 'b','fill');
line(cities(population(i,:),1), cities(population(i,:),2));
line(cities([population(i,1) population(i,end)],1), cities([population(i,1) population(i,end)],2));
axis([0 110 0 110]);

%close all;

What I want is to replace the tournament code with wheel and rank code.

Here is what I wrote for the Wheel Selection:

 fitness = tourDistance./sum(tourDistance);
        wheel = cumsum(fitness);
        parent1 = population(find(wheel >= rand(),1),:);
        parent2 = population(find(wheel >= rand(),1),:);

Solution

  • Here is a vectorized implementation of a roulette wheel selection in Matlab:

    [~,W] = min(ones(popSize,1)*rand(1,2*popSize) > ((cumsum(fitness)*ones(1,2*popSize)/sum(fitness))),[],1);
    

    This assumes that the fitness input into the selection scheme is a matrix of size (popSize x 1) (or a column vector of the same size as the number of population members).

    And popSize is obviously the amount of members in your population. And W is the winners or the population members that are selected to become parents/crossover.

    The output of the selection will be selected_parents which is a double row vector of size 2*popSize which has all of the indices of the members of the population that will be used in the crossover stage.

    This row vector can then be input into a vectorized crossover scheme that could look something like this:

    %% Single-Point Preservation Crossover
        Pop2 = Pop(W(1:2:end),:);                       % Pop2 Winners 1
        P2A  = Pop(W(2:2:end),:);                       % Pop2 Winners 2
        Lidx = sub2ind(size(Pop),[1:popSize]',round(rand(popSize,1)*(genome-1)+1));
        vLidx = P2A(Lidx)*ones(1,genome);
        [r,c]=find(Pop2==vLidx);
        [~,Ord]=sort(r);
        r = r(Ord); c = c(Ord);
        Lidx2 = sub2ind(size(Pop),r,c);
        Pop2(Lidx2) = Pop2(Lidx);
        Pop2(Lidx) = P2A(Lidx);
    

    this crossover assumes an input of the W variable from the selection scheme. It also uses Pop which is the population members stored in a popSize by genome matrix. (genome is the number of cities in one tour and also happens to be the size of the genome). The genome is stored as an array of integers with each integer being a city and the tour being defined as the order from the value of the genome array from the array's first index to the array's last index.

    while we are at it we may as well include a nice vectorized mutation scheme for a permuation genetic algorithm (which this is).

         %% Mutation (Permutation)
            idx = rand(popSize,1)<mutRate;
            Loc1 = sub2ind(size(Pop2),1:popSize,round(rand(1,popSize)*(genome-1)+1));
            Loc2 = sub2ind(size(Pop2),1:popSize,round(rand(1,popSize)*(genome-1)+1));
    
            Loc2(idx == 0) = Loc1(idx == 0);
            [Pop2(Loc1), Pop2(Loc2)] = deal(Pop2(Loc2), Pop2(Loc1));
    

    This mutation randomly flips the order of 2 cities in our tour (genome).

    Finally make sure to update your population after all of that work we did!

    %% Update Population!
    Pop = Pop2; % updates the population to include crossovers and mutation.
    

    So i know this reply is probably way too late for your assignment, but hopefully it will help someone else with a similar problem.

    I REALLY REALLY recommend anyone interested in vectorized genetic algorithms in Matlab to read this paper: UCL: Efficiently Vectorized Code for Population Based Optimization Algorithms

    It is what i based all of the code off of in the examples and it will teach you why you are writing the code that way. Its a great resource and what got me started with GAs.