001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.0). 003 * Copyright (c) 2007-2024 Franz Wilhelmstötter 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 * Author: 018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com) 019 */ 020package io.jenetics.ext.engine; 021 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.List; 026import java.util.Spliterator; 027import java.util.concurrent.atomic.AtomicReference; 028import java.util.function.Supplier; 029import java.util.stream.BaseStream; 030 031import io.jenetics.Gene; 032import io.jenetics.engine.EvolutionInit; 033import io.jenetics.engine.EvolutionResult; 034import io.jenetics.engine.EvolutionStart; 035import io.jenetics.engine.EvolutionStream; 036import io.jenetics.engine.EvolutionStreamable; 037import io.jenetics.internal.engine.EvolutionStreamImpl; 038 039import io.jenetics.ext.internal.util.ConcatSpliterator; 040 041/** 042 * The {@code ConcatEngine} lets you concatenate two (or more) evolution 043 * {@link io.jenetics.engine.Engine}, with different configurations, and let it 044 * use as <em>one</em> engine {@link EvolutionStreamable}. 045 * 046 * <pre> {@code 047 * +----------+ +----------+ 048 * | ES | | ES | 049 * +-------+----+ | +-------+----+ | 050 * (Start) | +-----+ Start | +-----+ 051 * ------>| Engine 1 |------------>| Engine 2 |-----------> 052 * | | Result | | Result 053 * +------------+ +------------+ 054 * } </pre> 055 * 056 * The sketch above shows how the engine concatenation works. In this example, 057 * the evolution stream of the first engine is evaluated until it terminates. 058 * The result of the first stream is then used as start input of the second 059 * evolution stream, which then delivers the final result. 060 * <p> 061 * Concatenating evolution engines might be useful, if you want to explore your 062 * search space with random search first and then start the <em>real</em> GA 063 * search. 064 * {@snippet lang="java": 065 * final Problem<double[], DoubleGene, Double> problem = Problem.of( 066 * v -> Math.sin(v[0])*Math.cos(v[1]), 067 * Codecs.ofVector(DoubleRange.of(0, 2*Math.PI), 2) 068 * ); 069 * 070 * final Engine<DoubleGene, Double> engine1 = Engine.builder(problem) 071 * .minimizing() 072 * .alterers(new Mutator<>(0.2)) 073 * .selector(new MonteCarloSelector<>()) 074 * .build(); 075 * 076 * final Engine<DoubleGene, Double> engine2 = Engine.builder(problem) 077 * .minimizing() 078 * .alterers( 079 * new Mutator<>(0.1), 080 * new MeanAlterer<>()) 081 * .selector(new RouletteWheelSelector<>()) 082 * .build(); 083 * 084 * final Genotype<DoubleGene> result = 085 * ConcatEngine.of( 086 * engine1.limit(50), 087 * engine2.limit(() -> Limits.bySteadyFitness(30))) 088 * .stream() 089 * .collect(EvolutionResult.toBestGenotype()); 090 * 091 * System.out.println(result + ": " + 092 * problem.fitness().apply(problem.codec().decode(result))); 093 * } 094 * 095 * An essential part, when concatenating evolution engines, is to make sure your 096 * engines are creating <em>limited</em> evolution streams. This is what 097 * the {@link EvolutionStreamable#limit(Supplier)} and 098 * {@link EvolutionStreamable#limit(long)} methods are for. Limiting an engine 099 * means, that this engine will surely create only streams, which are limited 100 * with the predicate/generation given to the engine. If you have limited your 101 * engines, it is no longer necessary to limit your final evolution stream, but 102 * you are still able to do so. 103 * 104 * @see CyclicEngine 105 * 106 * @param <G> the gene type 107 * @param <C> the fitness type 108 * 109 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 110 * @version 4.1 111 * @since 4.1 112 */ 113public final class ConcatEngine< 114 G extends Gene<?, G>, 115 C extends Comparable<? super C> 116> 117 extends EnginePool<G, C> 118{ 119 120 /** 121 * Create a new concatenating evolution engine with the given list of engines. 122 * 123 * @param engines the engines which are concatenated to <em>one</em> engine 124 * @throws NullPointerException if the {@code engines} or one of its 125 * elements is {@code null} 126 */ 127 public ConcatEngine(final List<? extends EvolutionStreamable<G, C>> engines) { 128 super(engines); 129 } 130 131 @Override 132 public EvolutionStream<G, C> 133 stream(final Supplier<EvolutionStart<G, C>> start) { 134 final AtomicReference<EvolutionStart<G, C>> other = 135 new AtomicReference<>(null); 136 137 return new EvolutionStreamImpl<>( 138 new ConcatSpliterator<>( 139 _engines.stream() 140 .map(engine -> engine 141 .stream(() -> start(start, other)) 142 .peek(result -> other.set(result.toEvolutionStart()))) 143 .map(BaseStream::spliterator) 144 .toList() 145 ), 146 false 147 ); 148 } 149 150 private EvolutionStart<G, C> start( 151 final Supplier<EvolutionStart<G, C>> first, 152 final AtomicReference<EvolutionStart<G, C>> other 153 ) { 154 return other.get() != null ? other.get() : first.get(); 155 } 156 157 @Override 158 public EvolutionStream<G, C> stream(final EvolutionInit<G> init) { 159 final AtomicReference<EvolutionStart<G, C>> other = 160 new AtomicReference<>(null); 161 162 return new EvolutionStreamImpl<>( 163 new ConcatSpliterator<>(spliterators(init, other)), 164 false 165 ); 166 } 167 168 private Collection<Spliterator<EvolutionResult<G, C>>> spliterators( 169 final EvolutionInit<G> init, 170 final AtomicReference<EvolutionStart<G, C>> other 171 ) { 172 final Collection<Spliterator<EvolutionResult<G, C>>> result; 173 if (_engines.isEmpty()) { 174 result = Collections.emptyList(); 175 } else if (_engines.size() == 1) { 176 result = List.of( 177 _engines.get(0) 178 .stream(init) 179 .peek(er -> other.set(er.toEvolutionStart())) 180 .spliterator() 181 ); 182 } else { 183 final List<Spliterator<EvolutionResult<G, C>>> concat = 184 new ArrayList<>(); 185 186 concat.add( 187 _engines.get(0) 188 .stream(init) 189 .peek(er -> other.set(er.toEvolutionStart())) 190 .spliterator() 191 ); 192 concat.addAll( 193 _engines.subList(1, _engines.size()).stream() 194 .map(engine -> engine 195 .stream(other::get) 196 .peek(er -> other.set(er.toEvolutionStart()))) 197 .map(BaseStream::spliterator) 198 .toList() 199 ); 200 201 result = concat; 202 } 203 204 return result; 205 } 206 207 /** 208 * Create a new concatenating evolution engine with the given array of 209 * engines. 210 * 211 * @param engines the engines which are concatenated to <em>one</em> engine 212 * @param <G> the gene type 213 * @param <C> the fitness type 214 * @return a new concatenating evolution engine 215 * @throws NullPointerException if the {@code engines} or one of its 216 * elements is {@code null} 217 */ 218 @SafeVarargs 219 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 220 ConcatEngine<G, C> of(final EvolutionStreamable<G, C>... engines) { 221 return new ConcatEngine<>(List.of(engines)); 222 } 223 224 225}