io.jenetics.ext.engine.ConcatEngine Maven / Gradle / Ivy
/*
* 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.BaseStream;
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.ConcatSpliterator;
/**
* The {@code ConcatEngine} lets you concatenate two (or more) evolution
* {@link io.jenetics.engine.Engine}, with different configurations, and let it
* use as one engine {@link EvolutionStreamable}.
*
* {@code
* +----------+ +----------+
* | ES | | ES |
* +-------+----+ | +-------+----+ |
* (Start) | +-----+ Start | +-----+
* ------>| Engine 1 |------------>| Engine 2 |----------->
* | | Result | | Result
* +------------+ +------------+
* }
*
* The sketch above shows how the engine concatenation works. In this example,
* the evolution stream of the first engine is evaluated until it terminates.
* The result of the first stream is then used as start input of the second
* evolution stream, which then delivers the final result.
*
* Concatenating evolution engines might be useful, if you want to explore your
* search space with random search first and then start the real GA
* search.
* {@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 =
* ConcatEngine.of(
* engine1.limit(50),
* engine2.limit(() -> Limits.bySteadyFitness(30)))
* .stream()
* .collect(EvolutionResult.toBestGenotype());
*
* System.out.println(result + ": " +
* problem.fitness().apply(problem.codec().decode(result)));
* }
*
* An essential part, when concatenating evolution engines, is to make sure your
* engines are creating limited evolution streams. This is what
* the {@link EvolutionStreamable#limit(Supplier)} and
* {@link EvolutionStreamable#limit(long)} methods are for. Limiting an engine
* means, that this engine will surely create only streams, which are limited
* with the predicate/generation given to the engine. If you have limited your
* engines, it is no longer necessary to limit your final evolution stream, but
* you are still able to do so.
*
* @see CyclicEngine
*
* @param the gene type
* @param the fitness type
*
* @author Franz Wilhelmstötter
* @version 4.1
* @since 4.1
*/
public final class ConcatEngine<
G extends Gene, G>,
C extends Comparable super C>
>
extends EnginePool
{
/**
* Create a new concatenating evolution engine with the given list of engines.
*
* @param engines the engines which are concatenated to one engine
* @throws NullPointerException if the {@code engines} or one of its
* elements is {@code null}
*/
public ConcatEngine(final List extends EvolutionStreamable> engines) {
super(engines);
}
@Override
public EvolutionStream
stream(final Supplier> start) {
final AtomicReference> other =
new AtomicReference<>(null);
return new EvolutionStreamImpl<>(
new ConcatSpliterator<>(
_engines.stream()
.map(engine -> engine
.stream(() -> start(start, other))
.peek(result -> other.set(result.toEvolutionStart())))
.map(BaseStream::spliterator)
.toList()
),
false
);
}
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 AtomicReference> other =
new AtomicReference<>(null);
return new EvolutionStreamImpl<>(
new ConcatSpliterator<>(spliterators(init, other)),
false
);
}
private Collection>> spliterators(
final EvolutionInit init,
final AtomicReference> other
) {
final Collection>> result;
if (_engines.isEmpty()) {
result = Collections.emptyList();
} else if (_engines.size() == 1) {
result = List.of(
_engines.get(0)
.stream(init)
.peek(er -> other.set(er.toEvolutionStart()))
.spliterator()
);
} else {
final List>> concat =
new ArrayList<>();
concat.add(
_engines.get(0)
.stream(init)
.peek(er -> other.set(er.toEvolutionStart()))
.spliterator()
);
concat.addAll(
_engines.subList(1, _engines.size()).stream()
.map(engine -> engine
.stream(other::get)
.peek(er -> other.set(er.toEvolutionStart())))
.map(BaseStream::spliterator)
.toList()
);
result = concat;
}
return result;
}
/**
* Create a new concatenating evolution engine with the given array of
* engines.
*
* @param engines the engines which are concatenated to one 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>>
ConcatEngine of(final EvolutionStreamable... engines) {
return new ConcatEngine<>(List.of(engines));
}
}