001 /*
002 * Java Genetic Algorithm Library (jenetics-7.1.0).
003 * Copyright (c) 2007-2022 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 */
020 package io.jenetics.ext.engine;
021
022 import java.util.List;
023 import java.util.Spliterator;
024 import java.util.concurrent.atomic.AtomicBoolean;
025 import java.util.concurrent.atomic.AtomicReference;
026 import java.util.function.Supplier;
027
028 import io.jenetics.Gene;
029 import io.jenetics.engine.EvolutionInit;
030 import io.jenetics.engine.EvolutionResult;
031 import io.jenetics.engine.EvolutionStart;
032 import io.jenetics.engine.EvolutionStream;
033 import io.jenetics.engine.EvolutionStreamable;
034 import io.jenetics.internal.engine.EvolutionStreamImpl;
035
036 import 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 an broad search-fine search-cycle
058 * as long as you want.
059 *
060 * <pre>{@code
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 * }</pre>
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 */
104 public 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 it's
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 }
|