001/*
002 * Java Genetic Algorithm Library (jenetics-7.2.0).
003 * Copyright (c) 2007-2023 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.util;
021
022import static java.lang.Math.max;
023import static java.util.Objects.requireNonNull;
024
025import java.util.Random;
026import java.util.function.Consumer;
027import java.util.function.Function;
028import java.util.function.Supplier;
029import java.util.random.RandomGenerator;
030import java.util.random.RandomGeneratorFactory;
031
032/**
033 * This class holds the {@link RandomGenerator} engine used for the GA. The
034 * {@code RandomRegistry} is thread safe and is initialized with the
035 * {@link RandomGeneratorFactory#getDefault()} PRNG.
036 *
037 * <h2>Setup the PRNG used for the evolution process</h2>
038 * There are several ways on how to set the {@link RandomGenerator} used during
039 * the evolution process.
040 * <p>
041 *
042 * <b>Using a {@link RandomGeneratorFactory}</b><br>
043 * The following example registers the <em>L128X1024MixRandom</em> random
044 * generator. By using a factory, each thread gets its own generator instance,
045 * which ensures thread-safety without the necessity of the created random
046 * generator to be thread-safe.
047 * <pre>{@code
048 * // This is the default setup.
049 * RandomRegistry.random(RandomGeneratorFactory.getDefault());
050 *
051 * // Using the "L128X1024MixRandom" random generator for the evolution.
052 * RandomRegistry.random(RandomGeneratorFactory.of("L128X1024MixRandom"));
053 * }</pre>
054 * <br>
055 *
056 * <b>Using a {@link RandomGenerator} {@link Supplier}</b><br>
057 * If you have a random engine, which is not available as
058 * {@link RandomGeneratorFactory}, it is also possible to register a
059 * {@link Supplier} of the desired random generator. This method has the same
060 * thread-safety property as the method above.
061 * <pre>{@code
062 * RandomRegistry.random(() -> new MySpecialRandomGenerator());
063 * }</pre>
064 *
065 * Register a random generator supplier is also more flexible. It allows
066 * using the streaming and splitting capabilities of the random generators
067 * implemented in the Java library.
068 * <pre>{@code
069 * final Iterator<RandomGenerator> randoms =
070 *     StreamableGenerator.of("L128X1024MixRandom")
071 *         .rngs()
072 *         .iterator();
073 *
074 * RandomRegistry.random(randoms::next);
075 * }</pre>
076 * <br>
077 *
078 * <b>Using a {@link RandomGenerator} instance</b><br>
079 * It is also possible to set a single random generator instance for the whole
080 * evolution process. When using this setup, the used random generator must be
081 * thread safe.
082 * <pre>{@code
083 * RandomRegistry.random(new Random(123456));
084 * }</pre>
085 * <p>
086 *
087 * The following code snippet shows an almost complete example of a typical
088 * random generator setup.
089 * <pre>{@code
090 * public class GA {
091 *     public static void main(final String[] args) {
092 *         // Initialize the registry with the factory of the PRGN.
093 *         final var factory = RandomGeneratorFactory.of("L128X1024MixRandom");
094 *         RandomRegistry.random(factory);
095 *
096 *         final Engine<DoubleGene, Double> engine = ...;
097 *         final EvolutionResult<DoubleGene, Double> result = engine.stream()
098 *             .limit(100)
099 *             .collect(toBestEvolutionResult());
100 *     }
101 * }
102 * }</pre>
103 *
104 * <h2>Setup of a <i>local</i> PRNG</h2>
105 *
106 * You can temporarily (and locally) change the implementation of the PRNG. E.g.,
107 * for initialize the engine stream with the same initial population.
108 *
109 * <pre>{@code
110 * public class GA {
111 *     public static void main(final String[] args) {
112 *         // Create a reproducible list of genotypes.
113 *         final var factory = RandomGeneratorFactory.of("L128X1024MixRandom");
114 *         final List<Genotype<DoubleGene>> genotypes =
115 *             with(factory.create(123), r ->
116 *                 Genotype.of(DoubleChromosome.of(0, 10)).instances()
117 *                     .limit(50)
118 *                     .collect(toList())
119 *             );
120 *
121 *         final Engine<DoubleGene, Double> engine = ...;
122 *         final EvolutionResult<DoubleGene, Double> result = engine
123 *              // Initialize the evolution stream with the given genotypes.
124 *             .stream(genotypes)
125 *             .limit(100)
126 *             .collect(toBestEvolutionResult());
127 *     }
128 * }
129 * }</pre>
130 *
131 * <p>
132 * The default random generator used by <em>Jenetics</em> is
133 * {@code L64X256MixRandom}. Via the system property
134 * {@code io.jenetics.util.defaultRandomGenerator}, it is possible to use a
135 * different random generator.
136 * <pre>{@code
137 * java -Dio.jenetics.util.defaultRandomGenerator=L64X1024MixRandom\
138 *      -cp jenetics-@__version__@.jar:app.jar\
139 *          com.foo.bar.MyJeneticsApp
140 * }</pre>
141 *
142 * @see RandomGenerator
143 * @see RandomGeneratorFactory
144 *
145 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
146 * @since 1.0
147 * @version 7.0
148 */
149public final class RandomRegistry {
150        private RandomRegistry() {}
151
152        /**
153         * Thread local wrapper for a random generator supplier (factory).
154         *
155         * @param <R> the type of the random generator
156         */
157        private static final class TLR<R extends RandomGenerator>
158                extends ThreadLocal<R>
159                implements Supplier<R>
160        {
161                private final Supplier<? extends R> _factory;
162
163                TLR(final Supplier<? extends R> factory) {
164                        _factory = requireNonNull(factory);
165                }
166
167                @Override
168                protected synchronized R initialValue() {
169                        return _factory.get();
170                }
171        }
172
173        private static final TLR<RandomGenerator> DEFAULT_RANDOM_FACTORY =
174                new TLR<>(RandomGeneratorFactory.of(Env.defaultRandomGenerator)::create);
175
176        private static final Context<Supplier<? extends RandomGenerator>> CONTEXT =
177                new Context<>(DEFAULT_RANDOM_FACTORY);
178
179        /**
180         * Return the {@link RandomGenerator} of the current scope.
181         *
182         * @return the {@link RandomGenerator} of the current scope
183         */
184        public static RandomGenerator random() {
185                return CONTEXT.get().get();
186        }
187
188        /**
189         * Set a new {@link RandomGenerator} for the <em>global</em> scope. The given
190         * {@link RandomGenerator} <b>must</b> be thread safe, which is the case for
191         * the Java {@link Random} class.
192         *
193         * @see #random(RandomGeneratorFactory)
194         *
195         * @param random the new {@link RandomGenerator} for the <em>global</em>
196         *        scope
197         * @throws NullPointerException if the {@code random} object is {@code null}
198         */
199        public static void random(final RandomGenerator random) {
200                requireNonNull(random);
201                CONTEXT.set(() -> random);
202        }
203
204        /**
205         * Set a new {@link RandomGeneratorFactory} for the <em>global</em> scope.
206         *
207         * @param factory the random generator factory
208         * @throws NullPointerException if the {@code factory} object is {@code null}.
209         */
210        public static <R extends RandomGenerator> void
211        random(final RandomGeneratorFactory<? extends R> factory) {
212                requireNonNull(factory);
213                CONTEXT.set(new TLR<>(factory::create));
214        }
215
216        /**
217         * Set a new {@link Supplier} of {@link RandomGenerator} for the
218         * <em>global</em> scope.
219         *
220         * @param supplier the random generator supplier
221         * @throws NullPointerException if the {@code supplier} object is {@code null}.
222         */
223        public static <R extends RandomGenerator> void
224        random(final Supplier<? extends R> supplier) {
225                requireNonNull(supplier);
226                CONTEXT.set(new TLR<>(supplier));
227        }
228
229        /**
230         * Set the random object to its default value.
231         */
232        public static void reset() {
233                CONTEXT.reset();
234        }
235
236        /**
237         * Executes the consumer code using the given {@code random} generator.
238         *
239         * <pre>{@code
240         * final MSeq<Integer> seq = ...
241         * using(new Random(123), r -> {
242         *     seq.shuffle();
243         * });
244         * }</pre>
245         *
246         * The example above shuffles the given integer {@code seq} <i>using</i> the
247         * given {@code Random(123)} engine.
248         *
249         * @since 3.0
250         *
251         * @param random the PRNG used within the consumer
252         * @param consumer the consumer which is executed with the <i>scope</i> of
253         *        the given {@code random} engine.
254         * @param <R> the type of the random engine
255         * @throws NullPointerException if one of the arguments is {@code null}
256         */
257        public static <R extends RandomGenerator> void using(
258                final R random,
259                final Consumer<? super R> consumer
260        ) {
261                CONTEXT.with(
262                        () -> random,
263                        r -> { consumer.accept(random); return null; }
264                );
265        }
266
267        /**
268         * Executes the consumer code using the given {@code random} generator.
269         *
270         * <pre>{@code
271         * final MSeq<Integer> seq = ...
272         * using(RandomGeneratorFactory.getDefault(), r -> {
273         *     seq.shuffle();
274         * });
275         * }</pre>
276         *
277         * The example above shuffles the given integer {@code seq} <i>using</i> the
278         * given {@link RandomGeneratorFactory#getDefault()} factory.
279         *
280         * @since 7.0
281         *
282         * @param factory the random generator factory used within the consumer
283         * @param consumer the consumer which is executed within the <i>scope</i> of
284         *        the given random generator.
285         * @param <R> the type of the random engine
286         * @throws NullPointerException if one of the arguments is {@code null}
287         */
288        public static <R extends RandomGenerator> void using(
289                final RandomGeneratorFactory<? extends R> factory,
290                final Consumer<? super R> consumer
291        ) {
292                CONTEXT.with(
293                        new TLR<>(factory::create),
294                        r -> { consumer.accept(r.get()); return null; }
295                );
296        }
297
298        /**
299         * Executes the consumer code using the given {@code random} generator
300         * supplier.
301         *
302         * <pre>{@code
303         * final MSeq<Integer> seq = ...
304         * using(() -> new MyRandomGenerator(), r -> {
305         *     seq.shuffle();
306         * });
307         * }</pre>
308         *
309         * @since 7.0
310         *
311         * @param supplier the random generator supplier used within the consumer
312         * @param consumer the consumer which is executed within the <i>scope</i> of
313         *        the given random generator.
314         * @param <R> the type of the random engine
315         * @throws NullPointerException if one of the arguments is {@code null}
316         */
317        public static <R extends RandomGenerator> void using(
318                final Supplier<? extends R> supplier,
319                final Consumer<? super R> consumer
320        ) {
321                CONTEXT.with(
322                        new TLR<>(supplier),
323                        r -> { consumer.accept(r.get()); return null; }
324                );
325        }
326
327        /**
328         * Opens a new <em>scope</em> with the given random generator and executes
329         * the given function within it. The following example shows how to create a
330         * reproducible list of genotypes:
331         * <pre>{@code
332         * final List<Genotype<DoubleGene>> genotypes =
333         *     with(new LCG64ShiftRandom(123), r ->
334         *         Genotype.of(DoubleChromosome.of(0, 10)).instances()
335         *            .limit(50)
336         *            .collect(toList())
337         *     );
338         * }</pre>
339         *
340         * @since 3.0
341         *
342         * @param <R> the type of the random engine
343         * @param <T> the function return type
344         * @param random the PRNG used for the opened scope
345         * @param function the function to apply within the random scope
346         * @return the object returned by the given function
347         * @throws NullPointerException if one of the arguments is {@code null}
348         */
349        public static <R extends RandomGenerator, T> T with(
350                final R random,
351                final Function<? super R, ? extends T> function
352        ) {
353                return CONTEXT.with(
354                        () -> random,
355                        s -> function.apply(random)
356                );
357        }
358
359        /**
360         * Opens a new <em>scope</em> with the given random generator factory and
361         * executes the given function within it.
362         * <pre>{@code
363         * final List<Genotype<DoubleGene>> genotypes =
364         *     with(RandomGeneratorFactory.getDefault(), random ->
365         *         Genotype.of(DoubleChromosome.of(0, 10)).instances()
366         *            .limit(50)
367         *            .collect(toList())
368         *     );
369         * }</pre>
370         *
371         * @since 3.0
372         *
373         * @param <R> the type of the random engine
374         * @param <T> the function return type
375         * @param factory the PRNG used for the opened scope
376         * @param function the function to apply within the random scope
377         * @return the object returned by the given function
378         * @throws NullPointerException if one of the arguments is {@code null}.
379         */
380        public static <R extends RandomGenerator, T> T with(
381                final RandomGeneratorFactory<? extends R> factory,
382                final Function<? super R, ? extends T> function
383        ) {
384                return CONTEXT.with(
385                        new TLR<>(factory::create),
386                        r -> function.apply(r.get())
387                );
388        }
389        /**
390         * Opens a new <em>scope</em> with the given random generator supplier and
391         * executes the given function within it.
392         * <pre>{@code
393         * final List<Genotype<DoubleGene>> genotypes =
394         *     with(() -> new MyRandomGenerator(), random ->
395         *         Genotype.of(DoubleChromosome.of(0, 10)).instances()
396         *            .limit(50)
397         *            .collect(toList())
398         *     );
399         * }</pre>
400         *
401         * @since 3.0
402         *
403         * @param <R> the type of the random engine
404         * @param <T> the function return type
405         * @param supplier the PRNG used for the opened scope
406         * @param function the function to apply within the random scope
407         * @return the object returned by the given function
408         * @throws NullPointerException if one of the arguments is {@code null}.
409         */
410
411        public static <R extends RandomGenerator, T> T with(
412                final Supplier<? extends R> supplier,
413                final Function<? super R, ? extends T> function
414        ) {
415                return CONTEXT.with(
416                        new TLR<>(supplier),
417                        r -> function.apply(r.get())
418                );
419        }
420
421        @SuppressWarnings("removal")
422        private static final class Env {
423                private static final String defaultRandomGenerator =
424                        java.security.AccessController.doPrivileged(
425                                (java.security.PrivilegedAction<String>)() ->
426                                        System.getProperty(
427                                                "io.jenetics.util.defaultRandomGenerator",
428                                                "L64X256MixRandom"
429                                        )
430                        );
431        }
432
433}