001/* 002 * Java Genetic Algorithm Library (jenetics-9.0.0). 003 * Copyright (c) 2007-2026 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.List; 023import java.util.Spliterator; 024import java.util.concurrent.atomic.AtomicBoolean; 025import java.util.concurrent.atomic.AtomicReference; 026import java.util.function.Supplier; 027 028import io.jenetics.Gene; 029import io.jenetics.engine.EvolutionInit; 030import io.jenetics.engine.EvolutionResult; 031import io.jenetics.engine.EvolutionStart; 032import io.jenetics.engine.EvolutionStream; 033import io.jenetics.engine.EvolutionStreamable; 034import io.jenetics.internal.engine.EvolutionStreamImpl; 035 036import io.jenetics.ext.internal.util.CyclicSpliterator; 037 038/** 039 * The {@code CyclicEngine} lets you concatenate two (or more) evolution 040 * {@link io.jenetics.engine.Engine}, with different configurations, and let it 041 * use as <em>one</em> engine {@link EvolutionStreamable}. If the last evolution 042 * stream terminates, it's <em>final</em> result is fed back to first engine. 043 * 044 * <pre> {@code 045 * +----------+ +----------+ 046 * | ES | | ES | 047 * +------------+ | +------------+ | 048 * (Start) | |-----+ Start | |-----+ 049 * ---+---->| Engine 1 |------------>| Engine 2 | --------+ 050 * ^ | | Result | | | 051 * | +------------+ +------------+ | 052 * | | 053 * +------------------------------<------------------------+ 054 * Result 055 * } </pre> 056 * 057 * The {@code CyclicEngine} allows to do a broad search-fine search-cycle 058 * as long as you want. 059 * {@snippet lang="java": 060 * final Problem<double[], DoubleGene, Double> problem = Problem.of( 061 * v -> Math.sin(v[0])*Math.cos(v[1]), 062 * Codecs.ofVector(new DoubleRange(0, 2*Math.PI), 2) 063 * ); 064 * 065 * final Engine<DoubleGene, Double> engine1 = Engine.builder(problem) 066 * .minimizing() 067 * .alterers(new Mutator<>(0.2)) 068 * .selector(new MonteCarloSelector<>()) 069 * .build(); 070 * 071 * final Engine<DoubleGene, Double> engine2 = Engine.builder(problem) 072 * .minimizing() 073 * .alterers( 074 * new Mutator<>(0.1), 075 * new MeanAlterer<>()) 076 * .selector(new RouletteWheelSelector<>()) 077 * .build(); 078 * 079 * final Genotype<DoubleGene> result = 080 * CyclicEngine.of( 081 * engine1.limit(50), 082 * engine2.limit(() -> Limits.bySteadyFitness(30))) 083 * .stream() 084 * .limit(Limits.bySteadyFitness(1000)) 085 * .collect(EvolutionResult.toBestGenotype()); 086 * 087 * System.out.println(result + ": " + 088 * problem.fitness().apply(problem.codec().decode(result))); 089 * } 090 * 091 * When using a {@code CyclicEnginePool}, you have to limit the final evolution 092 * stream, additionally to the defined limits on the used partial engines. 093 * 094 * @see ConcatEngine 095 * 096 * @param <G> the gene type 097 * @param <C> the fitness type 098 * 099 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 100 * @version 4.1 101 * @since 4.1 102 */ 103public final class CyclicEngine< 104 G extends Gene<?, G>, 105 C extends Comparable<? super C> 106> 107 extends EnginePool<G, C> 108{ 109 110 /** 111 * Create a new cycling evolution engine with the given list of 112 * {@code engines}. 113 * 114 * @param engines the evolution engines which are part of the cycling engine 115 * @throws NullPointerException if the {@code engines} or one of its 116 * elements is {@code null} 117 */ 118 public CyclicEngine( 119 final List<? extends EvolutionStreamable<G, C>> engines 120 ) { 121 super(engines); 122 } 123 124 @Override 125 public EvolutionStream<G, C> 126 stream(final Supplier<EvolutionStart<G, C>> start) { 127 final AtomicReference<EvolutionStart<G, C>> other = 128 new AtomicReference<>(null); 129 130 return new EvolutionStreamImpl<>( 131 new CyclicSpliterator<>( 132 _engines.stream() 133 .map(engine -> toSpliterator(engine, start, other)) 134 .toList() 135 ), 136 false 137 ); 138 } 139 140 private Supplier<Spliterator<EvolutionResult<G, C>>> toSpliterator( 141 final EvolutionStreamable<G, C> engine, 142 final Supplier<EvolutionStart<G, C>> start, 143 final AtomicReference<EvolutionStart<G, C>> other 144 ) { 145 return () -> engine.stream(() -> start(start, other)) 146 .peek(result -> other.set(result.toEvolutionStart())) 147 .spliterator(); 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 AtomicBoolean first = new AtomicBoolean(true); 160 final AtomicReference<EvolutionStart<G, C>> other = 161 new AtomicReference<>(null); 162 163 return new EvolutionStreamImpl<>( 164 new CyclicSpliterator<>( 165 _engines.stream() 166 .map(engine -> toSpliterator(engine, init, other, first)) 167 .toList() 168 ), 169 false 170 ); 171 } 172 173 private Supplier<Spliterator<EvolutionResult<G, C>>> toSpliterator( 174 final EvolutionStreamable<G, C> engine, 175 final EvolutionInit<G> init, 176 final AtomicReference<EvolutionStart<G, C>> other, 177 final AtomicBoolean first 178 ) { 179 return () -> { 180 if (first.get()) { 181 first.set(false); 182 return engine.stream(init) 183 .peek(result -> other.set(result.toEvolutionStart())) 184 .spliterator(); 185 } else { 186 return engine.stream(other::get) 187 .peek(result -> other.set(result.toEvolutionStart())) 188 .spliterator(); 189 } 190 }; 191 192 } 193 194 /** 195 * Create a new cycling evolution engine with the given array of 196 * {@code engines}. 197 * 198 * @param engines the evolution engines which are part of the cycling engine 199 * @param <G> the gene type 200 * @param <C> the fitness type 201 * @return a new concatenating evolution engine 202 * @throws NullPointerException if the {@code engines} or one of its 203 * elements is {@code null} 204 */ 205 @SafeVarargs 206 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 207 CyclicEngine<G, C> of(final EvolutionStreamable<G, C>... engines) { 208 return new CyclicEngine<>(List.of(engines)); 209 } 210 211}