Search code examples
javaframeworksgenetic-algorithmgenetic-programmingjenetics

Jenetics: how do I access evolution result (or statistics) at each step?


I am working on an optimization engine based on the Jenetics framework (link to jenetics.io). I have a evolution engine definition as follows:

final Thread engineStream = new Thread(() -> {
final MinMax<EvolutionResult<DoubleGene, Double>> best = MinMax.of();

_scannerEngine.stream()
    .limit(byPopulationConvergence(convergenceCriterion))
    .limit(result -> !Thread.currentThread().isInterrupted())                              
    .limit(maxGenerations)
    .peek(best).forEach(evolutionResult -> {
        waiting();
        if (callback != null) {
            callback.accept(evolutionResult, best.getMax());
        }
        final String generationCounter = String.valueOf(evolutionResult.getGeneration());
       _callingInstance.doOnAlgorithmCallback(new String("****** finished generation: " + generationCounter + " ****"));
    });
}

engineStream.start();

Currently, I am pushing at the end of one evolution cycle (one iteration through the population) a message to my UI via

peek(best).forEach(
    ...
    _callingInstance.doOnAlgorithmCallback(...)
)

What I would like to do is to push the current state of the evolution (ideally some statistics containing current fitness average, min, max, etc.) after having executed each fitness step, so the user can see the current state of the optimization process.

Any idea?


Final implementation based on Franz Wilhelmstötter's suggestions:

final Thread engineStream = new Thread(() -> {
    final MinMax<EvolutionResult<DoubleGene, Double>> best = MinMax.of();

    try {
        _scannerEngine.stream() //
        // stop stream if fitness average across the population is smaller than % of best fitness
        .limit(byFitnessConvergence(5, 20, convergence%)) //
        .limit(interrupted -> !Thread.currentThread().isInterrupted()) //
        .limit(_geneticScanParameters.getMaxGenerationsLimit()) // maximum number of generations
        .peek(best) //
        .peek(_statistics) //
        .forEach(evolutionResult -> {
            waiting();
            if (callback != null) {
                MessageLogger.logError(getClass(), best.getMax().toString());
                if (_geneticScanParameters.getOptimizationStrategy() == Optimize.MAXIMUM) {
                    callback.accept(evolutionResult, best.getMax());
                } else if (_geneticScanParameters.getOptimizationStrategy() == Optimize.MINIMUM) {
                    callback.accept(evolutionResult, best.getMin());
                }
            }
            MessageLogger.log(getClass(), _statistics.toString());
            final String generationCounter = String.valueOf(evolutionResult.getGeneration());
            _callingInstance.doOnAlgorithmCallback(new String("****** finished generation: " + generationCounter + " ****"));
        });

        MessageLogger.log(getClass(), "\n" + _statistics.toString());
    } catch (final Exception ex) {
    MessageLogger.logError(getClass(), Thread.currentThread(), ex);
    }
    _callingInstance.doOnAlgorithmFinished();
});
engineStream.start();
_engineStream = engineStream;

Please note that I use a convention where "_variable" are class attributes.


Solution

  • You can use the EvolutionStatistics class for this purpose.

    final Engine<DoubleGene, Double> engine = ...
    final EvolutionStatistics<Double, DoubleMomentStatistics> statistics =
        EvolutionStatistics.ofNumber();
    
    final Phenotype<DoubleGene, Double> result = engine.stream()
        .limit(bySteadyFitness(7))
        .limit(100)
        .peek(statistics)
        .collect(toBestPhenotype());
    
    System.println(statistics);
    

    If you update the class in the peek method you will get some additional statistics.