2

I have written a simple genetic algorithm program in java. What it is does is maximize the decimal value represented by the bits in the chromosome. Somehow mutation is not working as expected, e.g. causing two genes to mutate when just one is to change. The print statements I have included there show which to mutate, but in addition to that some more chromosomes get mutated. I can't figure out what the problem is :-(

Here are my java classes.

Gene.java

public class Gene {

    private int value;
    public Gene() {
        value = Math.random() < 0.5 ? 0 : 1;
    }

    public Gene(int value) {
        if (value != 0 && value != 1) {
            throw new IllegalArgumentException("value must be either 0 or 1");
        }
        else {
            this.value = value;
        }
    }

    public void mutate() {
        value = 1 - value;
    }

    public int value() {
        return value;
    }

    @Override
    public String toString() {
        return String.valueOf(value);
    }
}

Chromosome.java

import java.util.ArrayList;
import java.util.List;

public class Chromosome implements Comparable {

    private ArrayList<Gene> genes;
    private final int chromosomeLength;

    public Chromosome(int length) {
        this.genes = new ArrayList<>();

        this.chromosomeLength = length > 0 ? length : 16;

        for (int i = 0; i < chromosomeLength; i++) {
            this.genes.add(i, new Gene());
        }
    }

    public List<Gene> getAllele(int fromIndex, int toIndex) {
        return new ArrayList<>(genes.subList(fromIndex, toIndex));
    }

    public void setAllele(int fromIndex, List<Gene> allele) {

        int lastIndex = fromIndex + allele.size();
        if (lastIndex > chromosomeLength) {
            throw new IndexOutOfBoundsException("the allele exceeds beyond the size of the chromosome");
        }
        for (int i = fromIndex, j = 0; i < lastIndex; i++, j++) {
            genes.set(i, allele.get(j));
        }
    }

    public int getChromosomeLength() {
        return chromosomeLength;
    }

    public void setGeneAt(int index, Gene gene) {
        genes.set(index, gene);
    }

    public Gene getGeneAt(int index) {
        return genes.get(index);
    }

    public int value() {
        return Integer.parseInt(this.toString(), 2);
    }

    @Override
    public String toString() {
        StringBuilder chromosome = new StringBuilder("");

        genes.stream().forEach((Gene g) -> chromosome.append(g));

        return chromosome.toString();
    }

    @Override
    public int compareTo(Object anotherChromosome) {
        Chromosome c = (Chromosome) anotherChromosome;
        return this.value() - c.value();
    }
}

GenePool.java

import java.util.ArrayList;
import java.util.Arrays;

public class GenePool {

    private final ArrayList<Chromosome> genePool;
    private final int genePoolSize;
    private final int chromosomeLength;
    private final double crossOverRate;
    private final double mutationRate;
    private int[] crossPoints;

    public GenePool(int numOfChromosome, int chromosomeLength, double crossOverRate, double mutationRate) {

        this.genePoolSize = numOfChromosome;
        this.chromosomeLength = chromosomeLength > 0 ? chromosomeLength : 16;
        this.crossOverRate = crossOverRate;
        this.mutationRate = mutationRate;

        crossPoints = new int[1];
        crossPoints[0] = this.chromosomeLength / 2;

        genePool = new ArrayList<>();
        for (int i = 0; i < numOfChromosome; i++) {
            genePool.add(new Chromosome(chromosomeLength));
        }
    }

    public int getGenePoolSize() {
        return genePoolSize;
    }

    public Chromosome getChromosomeAt(int index) {
        return genePool.get(index);
    }

    public void setChromosomeAt(int index, Chromosome c) {
        genePool.set(index, c);
    }

    public int getChromosomeLength() {
        return chromosomeLength;
    }

    public Chromosome[] crossOver(Chromosome c1, Chromosome c2) {

        Chromosome[] offsprings = new Chromosome[2];
        offsprings[0] = new Chromosome(c1.getChromosomeLength());
        offsprings[1] = new Chromosome(c1.getChromosomeLength());

        Chromosome[] parentChromosomes = {c1, c2};

        int selector = 0;
        for (int i = 0, start = 0; i <= crossPoints.length; i++) {

            int crossPoint = i == crossPoints.length ? c1.getChromosomeLength() : crossPoints[i];

            offsprings[0].setAllele(start, parentChromosomes[selector].getAllele(start, crossPoint));
            offsprings[1].setAllele(start, parentChromosomes[1 - selector].getAllele(start, crossPoint));
            selector = 1 - selector;
            start = crossPoint;
        }
        return offsprings;
    }

    public void mutateGenePool() {

        int totalGeneCount = genePoolSize * chromosomeLength;

        System.out.println("Mutating genes:");
        for (int i = 0; i < totalGeneCount; i++) {
            double prob = Math.random();
            if (prob < mutationRate) {
                System.out.printf("Chromosome#: %d\tGene#: %d\n", i / chromosomeLength, i % chromosomeLength);
                genePool.get(i / chromosomeLength).getGeneAt(i % chromosomeLength).mutate();
            }
        }
        System.out.println("");
    }

    public int getLeastFitIndex() {
        int index = 0;
        int min = genePool.get(index).value();
        int currentValue;
        for (int i = 1; i < genePoolSize; i++) {
            currentValue = genePool.get(i).value();
            if (currentValue < min) {
                index = i;
                min = currentValue;
            }
        }
        return index;
    }

    public void saveFittest(ArrayList<Chromosome> offsprings) {
        // sort in ascending order
        offsprings.sort(null);

        offsprings.stream().forEach((offspring) -> {
            int leastFitIndex = getLeastFitIndex();
            if (offspring.value() > genePool.get(leastFitIndex).value()) {
                genePool.set(leastFitIndex, offspring);
            }
        });
    }

    public void evolve(int noOfGeneration) {

        for (int generation = 1; generation <= noOfGeneration; generation++) {

            System.out.println("Generation :" + generation);

            ArrayList<Integer> selection = new ArrayList<>();

            for (int i = 0; i < genePoolSize; i++) {
                if (Math.random() <= crossOverRate) {
                    selection.add(i);
                }
            }

            if (selection.size() % 2 == 1) {
                selection.remove(selection.size() - 1);
            }

            ArrayList<Chromosome> offsprings = new ArrayList<>();
            for (int i = 0; i < selection.size(); i += 2) {
                int index1 = selection.get(i);
                int index2 = selection.get(i + 1);
                offsprings.addAll(Arrays.asList(crossOver(genePool.get(index1), genePool.get(index2))));
            }

            System.out.println("Before saving the offsprings");
            displayChromosomes(genePool, "GenePool");
            displayChromosomes(offsprings, "Offsprings");

            saveFittest(offsprings);

            System.out.println("Before mutation:");
            displayChromosomes(genePool, "GenePool");

            mutateGenePool();

            System.out.println("After mutation:");
            displayChromosomes(genePool, "GenePool");

            System.out.println("\n\n");
        }
    }

    public void displayChromosomes(ArrayList<Chromosome> geneList, String name) {
        System.out.println(name);
        if (geneList.isEmpty()) {
            System.out.println("Empty list");
        }

        geneList.stream().forEach((c) -> {
            System.out.println(c + " -> " + c.value());
        });
        System.out.println("");

    }
}

GADemo.java

public class GADemo {


    public static void main(String[] args) {
        GenePool gp = new GenePool(6, 8, 0.25, 0.01);

        gp.evolve(10);

    }
}

After evolving for a number of generations, the chromosomes all tend to become exactly the same, or very similar. And the problem is that that value is not the maximum for that many bits, and sometimes even a small value. For example, for 8 bits the values should (tend to) approach 255, but this doesn't do so in my code. Someone please provide a hint where/how to look for and solve the problem.

Sнаđошƒаӽ
  • 16,753
  • 12
  • 73
  • 90

2 Answers2

4

Focus on these lines and imagine the references. These are from setAllele()

for (int i = fromIndex, j = 0; i < lastIndex; i++, j++) {
    genes.set(i, allele.get(j));
}

You are basically copying the reference from one onto the other. They are the same Gene so whatever mutation you do on those genes, will also affect even other Chromosomes.

You must produce a deep copy here.

Community
  • 1
  • 1
Rey Libutan
  • 5,226
  • 9
  • 42
  • 73
  • Thanks a lot. That silly thing easily slipped away from me, but not the maestro ;-) Anyway, I did just this: `genes.set(i, new Gene(allele.get(j).value()));`. Is it ok, or is there anything of bad practice or something? Please, I am a beginner, as you can see ;-) – Sнаđошƒаӽ Jul 13 '15 at 14:00
  • 1
    That's ok too but if your `Gene` has a `setValue()` method, you can eliminate `new Gene()` by doing `genes.get(i).setValue(allele.get(j).value())`. Basically, why overwrite the whole `Gene` when you just need to overwrite the value? – Rey Libutan Jul 13 '15 at 14:08
  • Thanks again! I will add a method `setValue` in `Gene` class. Wish I could upvote again :-) – Sнаđошƒаӽ Jul 13 '15 at 14:16
  • 1
    One more thing before I forgot, instead you might want to be consistent and name your functions as `getValue()` instead of `value()`. Just to be consistent as `setters and getters` which your IDE can also generate for you. – Rey Libutan Jul 13 '15 at 23:03
  • I have already done that when I added the method named `setValue` ;-) – Sнаđошƒаӽ Jul 14 '15 at 12:36
2

Initially each chromosome has an own list of genes. But when you do the crossover operation you set gene objects from one chromosome into the gene list of other chromosome.

When you evolve the system, the number of shared genes will rise and therefore ultimately all chromosomes will share the same genes. No matter how you mutate a gene the chromosomes are not affected.

EDIT: As Incognito also answered the setAllele method seems to be the culprit where gene sharing starts. You may want to introduce a method in the gene class where you can set its value given another gene.

wero
  • 32,544
  • 3
  • 59
  • 84