/* * Java Genetic Algorithm Library (@__identifier__@). * Copyright (c) @__year__@ 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 java.util.stream.Collectors; 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.ConcatSpliterator; /** * The {@code ConcatEngine} lets you concatenate two (or more) evolution * {@link io.jenetics.engine.Engine}, with different configurations, and let it * use as <em>one</em> engine {@link EvolutionStreamable}. * * <pre> {@code * +----------+ +----------+ * | ES | | ES | * +-------+----+ | +-------+----+ | * (Start) | +-----+ Start | +-----+ * ------>| Engine 1 |------------>| Engine 2 |-----------> * | | Result | | Result * +------------+ +------------+ * }</pre> * * 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. * <p> * Concatenating evolution engines might be useful, if you want to explore your * search space with random search first and then start the <em>real</em> GA * search. * <pre>{@code * final Problem<double[], DoubleGene, Double> problem = Problem.of( * v -> Math.sin(v[0])*Math.cos(v[1]), * Codecs.ofVector(DoubleRange.of(0, 2*Math.PI), 2) * ); * * final Engine<DoubleGene, Double> engine1 = Engine.builder(problem) * .minimizing() * .alterers(new Mutator<>(0.2)) * .selector(new MonteCarloSelector<>()) * .build(); * * final Engine<DoubleGene, Double> engine2 = Engine.builder(problem) * .minimizing() * .alterers( * new Mutator<>(0.1), * new MeanAlterer<>()) * .selector(new RouletteWheelSelector<>()) * .build(); * * final Genotype<DoubleGene> 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))); * }</pre> * * An essential part, when concatenating evolution engines, is to make sure your * your engines are creating <em>limited</em> 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 * your are still able to do so. * * @see CyclicEngine * * @param <G> the gene type * @param <C> the fitness type * * @author <a href="mailto:[email protected]">Franz Wilhelmstötter</a> * @version 4.1 * @since 4.1 */ public final class ConcatEngine< G extends Gene<?, G>, C extends Comparable<? super C> > extends EnginePool<G, C> { /** * Create a new concatenating evolution engine with the given list of engines. * * @param engines the engines which are concatenated to <em>one</em> engine * @throws NullPointerException if the {@code engines} or one of it's * elements is {@code null} */ public ConcatEngine(final List<? extends EvolutionStreamable<G, C>> engines) { super(engines); } @Override public EvolutionStream<G, C> stream(final Supplier<EvolutionStart<G, C>> start) { final AtomicReference<EvolutionStart<G, C>> 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) .collect(Collectors.toList()) ), false ); } private EvolutionStart<G, C> start( final Supplier<EvolutionStart<G, C>> first, final AtomicReference<EvolutionStart<G, C>> other ) { return other.get() != null ? other.get() : first.get(); } @Override public EvolutionStream<G, C> stream(final EvolutionInit<G> init) { final AtomicReference<EvolutionStart<G, C>> other = new AtomicReference<>(null); return new EvolutionStreamImpl<>( new ConcatSpliterator<>(spliterators(init, other)), false ); } private Collection<Spliterator<EvolutionResult<G, C>>> spliterators( final EvolutionInit<G> init, final AtomicReference<EvolutionStart<G, C>> other ) { final Collection<Spliterator<EvolutionResult<G, C>>> 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<Spliterator<EvolutionResult<G, C>>> 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) .collect(Collectors.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 <em>one</em> engine * @param <G> the gene type * @param <C> the fitness type * @return a new concatenating evolution engine * @throws NullPointerException if the {@code engines} or one of it's * elements is {@code null} */ @SafeVarargs public static <G extends Gene<?, G>, C extends Comparable<? super C>> ConcatEngine<G, C> of(final EvolutionStreamable<G, C>... engines) { return new ConcatEngine<>(List.of(engines)); } }