jenetics: Fitness: Assign an object to the determination of fitness & complex fitness result type

117 views Asked by At

I am using an IntegerChromosome which is an encoding for the actual problem. To determine the fitness I have to decode from the IntegerChromosome, or more the IntegerGene values, to the actual base type for the optimization problem. So, e.g. IntegerGene value 0 means an instance of type Class with values A, 1 means an instance of type Class with values B and so on. So, 01233210 would translate to ABCDDCBA. Only the latter I can evaluate. I get this information at runtime in a class FitnessInput.

Therefore I need to pass FitnessInput to the determination of the fitness function. Looking at a simple example I found that the determination of the fitness, in the eval() method in the example, takes place in a static method. Is there a concept and a related example how to pass runtime objects to the determination of the fitness rather than overwriting a static variable in the class where fitness() is implemented?


A second question related to the problem of fitness determination. I found examples where simple data types Integer, Double are used for the determination of the fitness. While this is of course reasonable, I would like to return an object to the user for the best phenotype, which contains all intermediate results for the determination of its fitness. I guess this should be possible if my return object implements Comparable. How can I make use, e.g., of the Function interface for that?

1

There are 1 answers

4
Franz Wilhelmstötter On

You might have a look at the Codec interface. This interface is responsible for converting the objects of your problem domain into a Genotype. If I understand your problem correctly, a possible encoding might look like this:

static final List<String> OBJECTS = List.of("A", "B", "C", "D");
static final int LENGTH = 10;

static final Codec<List<String>, IntegerGene> CODEC = Codec.of(
    // Create the Genotype the GA is working with.
    Genotype.of(IntegerChromosome.of(0, OBJECTS.size() - 1, LENGTH)),
    // Convert the Genotype back to your problem domain.
    gt -> gt.chromosome().stream()
        .map(IntegerGene::allele)
        .map(OBJECTS::get)
        .collect(Collectors.toList())
);

// Calculate the fitness function in terms of your problem domain.
static double fitness(final List<String> objects) {
    return 0;
}

static final Engine<IntegerGene, Double> ENGINE = Engine
    .builder(Main::fitness, CODEC)
    .build();

The Codec creates a Genotype which consists of an IntegerChromosome of length 10 and converts it back to your problem domain.

I'm not sure if I understood your second question correctly. But if you want to collect the intermediate results as well, you can use the Stream::peek method.

public static void main(final String[] args) {
    final List<Phenotype<IntegerGene, Double>> intermediateResults = new ArrayList<>();
    
    final var bestPhenotype = ENGINE.stream()
        .limit(Limits.bySteadyFitness(25))
        .peek(er -> intermediateResults.add(er.bestPhenotype()))
        .collect(EvolutionResult.toBestPhenotype());
}