io.jenetics.ext.engine.CyclicEngine Maven / Gradle / Ivy
The newest version!
/*
* Java Genetic Algorithm Library (jenetics-8.1.0).
* Copyright (c) 2007-2024 Franz Wilhelmstötter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author:
* Franz Wilhelmstötter ([email protected])
*/
package io.jenetics.ext.engine;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import io.jenetics.Gene;
import io.jenetics.engine.EvolutionInit;
import io.jenetics.engine.EvolutionResult;
import io.jenetics.engine.EvolutionStart;
import io.jenetics.engine.EvolutionStream;
import io.jenetics.engine.EvolutionStreamable;
import io.jenetics.internal.engine.EvolutionStreamImpl;
import io.jenetics.ext.internal.util.CyclicSpliterator;
/**
* The {@code CyclicEngine} lets you concatenate two (or more) evolution
* {@link io.jenetics.engine.Engine}, with different configurations, and let it
* use as one engine {@link EvolutionStreamable}. If the last evolution
* stream terminates, it's final result is fed back to first engine.
*
* {@code
* +----------+ +----------+
* | ES | | ES |
* +------------+ | +------------+ |
* (Start) | |-----+ Start | |-----+
* ---+---->| Engine 1 |------------>| Engine 2 | --------+
* ^ | | Result | | |
* | +------------+ +------------+ |
* | |
* +------------------------------<------------------------+
* Result
* }
*
* The {@code CyclicEngine} allows to do a broad search-fine search-cycle
* as long as you want.
*
* {@snippet lang="java":
* final Problem problem = Problem.of(
* v -> Math.sin(v[0])*Math.cos(v[1]),
* Codecs.ofVector(DoubleRange.of(0, 2*Math.PI), 2)
* );
*
* final Engine engine1 = Engine.builder(problem)
* .minimizing()
* .alterers(new Mutator<>(0.2))
* .selector(new MonteCarloSelector<>())
* .build();
*
* final Engine engine2 = Engine.builder(problem)
* .minimizing()
* .alterers(
* new Mutator<>(0.1),
* new MeanAlterer<>())
* .selector(new RouletteWheelSelector<>())
* .build();
*
* final Genotype result =
* CyclicEngine.of(
* engine1.limit(50),
* engine2.limit(() -> Limits.bySteadyFitness(30)))
* .stream()
* .limit(Limits.bySteadyFitness(1000))
* .collect(EvolutionResult.toBestGenotype());
*
* System.out.println(result + ": " +
* problem.fitness().apply(problem.codec().decode(result)));
* }
*
* When using a {@code CyclicEnginePool}, you have to limit the final evolution
* stream, additionally to the defined limits on the used partial engines.
*
* @see ConcatEngine
*
* @param the gene type
* @param the fitness type
*
* @author Franz Wilhelmstötter
* @version 4.1
* @since 4.1
*/
public final class CyclicEngine<
G extends Gene, G>,
C extends Comparable super C>
>
extends EnginePool
{
/**
* Create a new cycling evolution engine with the given list of
* {@code engines}.
*
* @param engines the evolution engines which are part of the cycling engine
* @throws NullPointerException if the {@code engines} or one of its
* elements is {@code null}
*/
public CyclicEngine(
final List extends EvolutionStreamable> engines
) {
super(engines);
}
@Override
public EvolutionStream
stream(final Supplier> start) {
final AtomicReference> other =
new AtomicReference<>(null);
return new EvolutionStreamImpl<>(
new CyclicSpliterator<>(
_engines.stream()
.map(engine -> toSpliterator(engine, start, other))
.toList()
),
false
);
}
private Supplier>> toSpliterator(
final EvolutionStreamable engine,
final Supplier> start,
final AtomicReference> other
) {
return () -> engine.stream(() -> start(start, other))
.peek(result -> other.set(result.toEvolutionStart()))
.spliterator();
}
private EvolutionStart start(
final Supplier> first,
final AtomicReference> other
) {
return other.get() != null ? other.get() : first.get();
}
@Override
public EvolutionStream stream(final EvolutionInit init) {
final AtomicBoolean first = new AtomicBoolean(true);
final AtomicReference> other =
new AtomicReference<>(null);
return new EvolutionStreamImpl<>(
new CyclicSpliterator<>(
_engines.stream()
.map(engine -> toSpliterator(engine, init, other, first))
.toList()
),
false
);
}
private Supplier>> toSpliterator(
final EvolutionStreamable engine,
final EvolutionInit init,
final AtomicReference> other,
final AtomicBoolean first
) {
return () -> {
if (first.get()) {
first.set(false);
return engine.stream(init)
.peek(result -> other.set(result.toEvolutionStart()))
.spliterator();
} else {
return engine.stream(other::get)
.peek(result -> other.set(result.toEvolutionStart()))
.spliterator();
}
};
}
/**
* Create a new cycling evolution engine with the given array of
* {@code engines}.
*
* @param engines the evolution engines which are part of the cycling engine
* @param the gene type
* @param the fitness type
* @return a new concatenating evolution engine
* @throws NullPointerException if the {@code engines} or one of its
* elements is {@code null}
*/
@SafeVarargs
public static , C extends Comparable super C>>
CyclicEngine of(final EvolutionStreamable... engines) {
return new CyclicEngine<>(List.of(engines));
}
}