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}