Search code examples
java-streamjenetics

jenetics: Set EvolutionStream limit outside of stream()


There are several possibilities in jenetics to set termination limits to EvolutionStream, see the documentation.

The limits are usually applied directly on the stream, e.g.

Phenotype<IntegerGene,Double> result = engine.stream()
    .limit(Limits.bySteadyFitness(10))
    .collect(EvolutionResult.toBestPhenotype());

or

Phenotype<IntegerGene,Double> result = engine.stream()
    .limit(Limits.byFixedGeneration(10))
    .collect(EvolutionResult.toBestPhenotype());

or in combination, see example:

Phenotype<IntegerGene,Double> result = engine.stream()
    .limit(Limits.bySteadyFitness(10))
    .limit(Limits.byFixedGeneration(10))
    .collect(EvolutionResult.toBestPhenotype());

In my optimization problem, I want to let the user decide which limits to assign to the problem. I do not know the limit setup in advance. It might be multiple limits. Therefore, I have to assign the limit types at runtime.

I tried to create a EvolutionStream object by

EvolutionStream<IntegerGene, Double> evolutionStream = engine.stream();

and assign the limits on the evolutionStream:

Stream<EvolutionResult<IntegerGene, Double>> limit = evolutionStream.limit(Limits.byFixedGeneration(10));

The result is a Stream, which does not know the EvolutionStream specific limit methods. Thus, I can not apply it in case multiple limits are defined. Trying to cast

evolutionStream = (EvolutionStream<IntegerGene, Double>)evolutionStream.limit(Limits.byFixedGeneration(10));

results in an error:

java.lang.ClassCastException: class java.util.stream.SliceOps$1 cannot be cast to class io.jenetics.engine.EvolutionStream (java.util.stream.SliceOps$1 is in module java.base of loader 'bootstrap'; io.jenetics.engine.EvolutionStream is in unnamed module of loader 'app')

So, is there a way to properly apply multiple limits outside the stream builder?


Solution

  • The EvolutionStream.limit(Predicate) method does return an EvolutionStream.

    EvolutionStream<IntegerGene, Double> stream = engine.stream();
    stream = stream
        .limit(Limits.byFixedGeneration(10))
        .limit(Limits.bySteadyFitness(5))
        .limit(Limits.byExecutionTime(Duration.ofMillis(100)));
    

    So your given examples look good and should work. But the EvolutionStream.limit(Predicate) method is the only method which gives you back an EvolutionStream.

    An alternative would be that your method, which initializes the EvolutionStream, takes the Predicates from outside.

    @SafeVarargs
    static EvolutionStream<IntegerGene, Double>
    newStream(final Predicate<? super EvolutionResult<IntegerGene, Double>>... limits) {
        final Engine<IntegerGene, Double> engine = Engine
            .builder(a -> a.gene().allele().doubleValue(), IntegerChromosome.of(0, 100))
            .build();
    
        EvolutionStream<IntegerGene, Double> stream = engine.stream();
        for (var limit : limits) {
            stream = stream.limit(limit);
        }
    
        return stream;
    }    
        
    final var stream = newStream(
        Limits.byFixedGeneration(100),
        Limits.byExecutionTime(Duration.ofMillis(1000)),
        Limits.bySteadyFitness(10)
    );