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