Search code examples
javagenetic-algorithmgeneticjenetics

Jenetics custom gene/chromosome


I have started experimenting with the Jenetics library, however I am having some issues with trying to make a very easy "custom" set of gene/chromosomes. What I tried to do was to create a custom chromosome with a different (random) number of custom genes inside. The genes simply contain an integer value, just for the sake of simplicity. For the same simplicity, the contents of a gene can only be numbers ranging from 0 to 9 and a Gene is considered valid only if it does NOT contain the number 9 (again, retardedly simple, but I just wanted to make them custom)

Here is my code:

CustomGene:

public class CustomGene implements Gene<Integer, CustomGene> {

    private Integer value;

    private CustomGene(Integer value) {
        this.value = value;
    }

    public static CustomGene of(Integer value) {
        return new CustomGene(value);
    }

    public static ISeq<CustomGene> seq(Integer min, Integer max, int length) {
        Random r = RandomRegistry.getRandom();
        return MSeq.<CustomGene>ofLength(length).fill(() ->
                new CustomGene(random.nextInt(r, min, max))
        ).toISeq();
    }

    @Override
    public Integer getAllele() {
        return value;
    }

    @Override
    public CustomGene newInstance() {
        final Random random = RandomRegistry.getRandom();
        return new CustomGene(Math.abs(random.nextInt(9)));
    }

    @Override
    public CustomGene newInstance(Integer integer) {
        return new CustomGene(integer);
    }

    @Override
    public boolean isValid() {
        return value != 9;
    }
}

CustomChromosome:

import org.jenetics.Chromosome;
import org.jenetics.util.ISeq;
import org.jenetics.util.RandomRegistry;

import java.util.Iterator;
import java.util.Random;

public class CustomChromosome implements Chromosome<CustomGene> {

    private ISeq<CustomGene> iSeq;
    private final int length;

    public CustomChromosome(ISeq<CustomGene> genes) {
        this.iSeq = genes;
        this.length = iSeq.length();
    }

    public static CustomChromosome of(ISeq<CustomGene> genes) {
        return new CustomChromosome(genes);
    }

    @Override
    public Chromosome<CustomGene> newInstance(ISeq<CustomGene> iSeq) {
        this.iSeq = iSeq;
        return this;
    }

    @Override
    public CustomGene getGene(int i) {
        return iSeq.get(i);
    }

    @Override
    public int length() {
        return iSeq.length();
    }

    @Override
    public ISeq<CustomGene> toSeq() {
        return iSeq;
    }

    @Override
    public Chromosome<CustomGene> newInstance() {
        final Random random = RandomRegistry.getRandom();
        ISeq<CustomGene> genes = ISeq.empty();
        for (int i = 0; i < length; i++) {
            genes = genes.append(CustomGene.of(Math.abs(random.nextInt(9))));
        }
        return new CustomChromosome(genes);
    }

    @Override
    public Iterator<CustomGene> iterator() {
        return iSeq.iterator();
    }

    @Override
    public boolean isValid() {
        return iSeq.stream().allMatch(CustomGene::isValid);
    }
}

Main:

import org.jenetics.Genotype;
import org.jenetics.Optimize;
import org.jenetics.engine.Engine;
import org.jenetics.engine.EvolutionResult;
import org.jenetics.util.Factory;
import org.jenetics.util.RandomRegistry;

import java.util.Random;

public class Main {

    private static int maxSum = - 100;

    private static Integer eval(Genotype<CustomGene> gt) {
        final int sum = gt.getChromosome().stream().mapToInt(CustomGene::getAllele).sum();
        if(sum > maxSum)
            maxSum = sum;
        return sum;

    }

    public static void main(String[] args) {
        final Random random = RandomRegistry.getRandom();

        Factory<Genotype<CustomGene>> g =
                Genotype.of(CustomChromosome.of(CustomGene.seq(0, 9, Math.abs(random.nextInt(9)) + 1)));

        Engine<CustomGene, Integer> engine = Engine
                .builder(Main::eval, g)
                .optimize(Optimize.MAXIMUM)
                .populationSize(100)
                .build();

        Genotype<CustomGene> result = engine.stream().limit(10000)
                .collect(EvolutionResult.toBestGenotype());

        System.out.println(eval(result));
        result.getChromosome().stream().forEach(i -> {
            System.out.print(i.getAllele() + " ");
        });

        System.out.println();

        System.out.println(maxSum);
    }
}

I do not understand why I get this output:

13 (evaluated result)
1 8 0 4 (all the alleles form the genes of the chosen chromosome)
32 (the actual maximum fitness found)

We can clearly see a difference between the genotype which had the biggest fitness function and the chosen genotype. Why? I know I'm doing something wrong and it's probably a silly mistake, but I really can't seem to understand what I am doing wrong. Could you please help me out?

Lots of thanks!


Solution

  • As posted by the creator of the library here, the answer was:

    you violated the contract of the Chromosome.newInstance(ISeq) method. This method must return a new chromosome instance. After fixing this

    @Override
    public Chromosome<CustomGene> newInstance(ISeq<CustomGene> iSeq) {
        return new CustomChromosome(iSeq);
    }