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}