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.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 * 060 * {@snippet lang="java": 061 * final Problem<double[], DoubleGene, Double> problem = Problem.of( 062 * v -> Math.sin(v[0])*Math.cos(v[1]), 063 * Codecs.ofVector(DoubleRange.of(0, 2*Math.PI), 2) 064 * ); 065 * 066 * final Engine<DoubleGene, Double> engine1 = Engine.builder(problem) 067 * .minimizing() 068 * .alterers(new Mutator<>(0.2)) 069 * .selector(new MonteCarloSelector<>()) 070 * .build(); 071 * 072 * final Engine<DoubleGene, Double> engine2 = Engine.builder(problem) 073 * .minimizing() 074 * .alterers( 075 * new Mutator<>(0.1), 076 * new MeanAlterer<>()) 077 * .selector(new RouletteWheelSelector<>()) 078 * .build(); 079 * 080 * final Genotype<DoubleGene> result = 081 * CyclicEngine.of( 082 * engine1.limit(50), 083 * engine2.limit(() -> Limits.bySteadyFitness(30))) 084 * .stream() 085 * .limit(Limits.bySteadyFitness(1000)) 086 * .collect(EvolutionResult.toBestGenotype()); 087 * 088 * System.out.println(result + ": " + 089 * problem.fitness().apply(problem.codec().decode(result))); 090 * } 091 * 092 * When using a {@code CyclicEnginePool}, you have to limit the final evolution 093 * stream, additionally to the defined limits on the used partial engines. 094 * 095 * @see ConcatEngine 096 * 097 * @param <G> the gene type 098 * @param <C> the fitness type 099 * 100 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 101 * @version 4.1 102 * @since 4.1 103 */ 104public final class CyclicEngine< 105 G extends Gene<?, G>, 106 C extends Comparable<? super C> 107> 108 extends EnginePool<G, C> 109{ 110 111 /** 112 * Create a new cycling evolution engine with the given list of 113 * {@code engines}. 114 * 115 * @param engines the evolution engines which are part of the cycling engine 116 * @throws NullPointerException if the {@code engines} or one of its 117 * elements is {@code null} 118 */ 119 public CyclicEngine( 120 final List<? extends EvolutionStreamable<G, C>> engines 121 ) { 122 super(engines); 123 } 124 125 @Override 126 public EvolutionStream<G, C> 127 stream(final Supplier<EvolutionStart<G, C>> start) { 128 final AtomicReference<EvolutionStart<G, C>> other = 129 new AtomicReference<>(null); 130 131 return new EvolutionStreamImpl<>( 132 new CyclicSpliterator<>( 133 _engines.stream() 134 .map(engine -> toSpliterator(engine, start, other)) 135 .toList() 136 ), 137 false 138 ); 139 } 140 141 private Supplier<Spliterator<EvolutionResult<G, C>>> toSpliterator( 142 final EvolutionStreamable<G, C> engine, 143 final Supplier<EvolutionStart<G, C>> start, 144 final AtomicReference<EvolutionStart<G, C>> other 145 ) { 146 return () -> engine.stream(() -> start(start, other)) 147 .peek(result -> other.set(result.toEvolutionStart())) 148 .spliterator(); 149 } 150 151 private EvolutionStart<G, C> start( 152 final Supplier<EvolutionStart<G, C>> first, 153 final AtomicReference<EvolutionStart<G, C>> other 154 ) { 155 return other.get() != null ? other.get() : first.get(); 156 } 157 158 @Override 159 public EvolutionStream<G, C> stream(final EvolutionInit<G> init) { 160 final AtomicBoolean first = new AtomicBoolean(true); 161 final AtomicReference<EvolutionStart<G, C>> other = 162 new AtomicReference<>(null); 163 164 return new EvolutionStreamImpl<>( 165 new CyclicSpliterator<>( 166 _engines.stream() 167 .map(engine -> toSpliterator(engine, init, other, first)) 168 .toList() 169 ), 170 false 171 ); 172 } 173 174 private Supplier<Spliterator<EvolutionResult<G, C>>> toSpliterator( 175 final EvolutionStreamable<G, C> engine, 176 final EvolutionInit<G> init, 177 final AtomicReference<EvolutionStart<G, C>> other, 178 final AtomicBoolean first 179 ) { 180 return () -> { 181 if (first.get()) { 182 first.set(false); 183 return engine.stream(init) 184 .peek(result -> other.set(result.toEvolutionStart())) 185 .spliterator(); 186 } else { 187 return engine.stream(other::get) 188 .peek(result -> other.set(result.toEvolutionStart())) 189 .spliterator(); 190 } 191 }; 192 193 } 194 195 /** 196 * Create a new cycling evolution engine with the given array of 197 * {@code engines}. 198 * 199 * @param engines the evolution engines which are part of the cycling engine 200 * @param <G> the gene type 201 * @param <C> the fitness type 202 * @return a new concatenating evolution engine 203 * @throws NullPointerException if the {@code engines} or one of its 204 * elements is {@code null} 205 */ 206 @SafeVarargs 207 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 208 CyclicEngine<G, C> of(final EvolutionStreamable<G, C>... engines) { 209 return new CyclicEngine<>(List.of(engines)); 210 } 211 212}