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.util;
021
022import static java.util.Objects.requireNonNull;
023
024import java.util.Comparator;
025import java.util.Optional;
026import java.util.Random;
027import java.util.function.Consumer;
028import java.util.function.Function;
029import java.util.function.Supplier;
030import java.util.random.RandomGenerator;
031import java.util.random.RandomGeneratorFactory;
032
033/**
034 * This class holds the {@link RandomGenerator} engine used for the GA. The
035 * {@code RandomRegistry} is thread safe and is initialized with the
036 * {@link RandomGeneratorFactory#getDefault()} PRNG.
037 *
038 * <h2>Setup the PRNG used for the evolution process</h2>
039 * There are several ways on how to set the {@link RandomGenerator} used during
040 * the evolution process.
041 * <p>
042 *
043 * <b>Using a {@link RandomGeneratorFactory}</b><br>
044 * The following example registers the <em>L128X1024MixRandom</em> random
045 * generator. By using a factory, each thread gets its own generator instance,
046 * which ensures thread-safety without the necessity of the created random
047 * generator to be thread-safe.
048 * {@snippet lang="java":
049 * // This is the default setup.
050 * RandomRegistry.random(RandomGeneratorFactory.getDefault());
051 *
052 * // Using the "L128X1024MixRandom" random generator for the evolution.
053 * RandomRegistry.random(RandomGeneratorFactory.of("L128X1024MixRandom"));
054 * }
055 * <br>
056 *
057 * <b>Using a {@link RandomGenerator} {@link Supplier}</b><br>
058 * If you have a random engine, which is not available as
059 * {@link RandomGeneratorFactory}, it is also possible to register a
060 * {@link Supplier} of the desired random generator. This method has the same
061 * thread-safety property as the method above.
062 * {@snippet lang="java":
063 * RandomRegistry.random(() -> new MySpecialRandomGenerator());
064 * }
065 *
066 * Register a random generator supplier is also more flexible. It allows
067 * using the streaming and splitting capabilities of the random generators
068 * implemented in the Java library.
069 * {@snippet lang="java":
070 * final Iterator<RandomGenerator> randoms =
071 *     StreamableGenerator.of("L128X1024MixRandom")
072 *         .rngs()
073 *         .iterator();
074 *
075 * RandomRegistry.random(randoms::next);
076 * }
077 * <br>
078 *
079 * <b>Using a {@link RandomGenerator} instance</b><br>
080 * It is also possible to set a single random generator instance for the whole
081 * evolution process. When using this setup, the used random generator must be
082 * thread safe.
083 * {@snippet lang="java":
084 * RandomRegistry.random(new Random(123456));
085 * }
086 * <p>
087 *
088 * The following code snippet shows an almost complete example of a typical
089 * random generator setup.
090 * {@snippet lang="java":
091 * public class GA {
092 *     public static void main(final String[] args) {
093 *         // Initialize the registry with the factory of the PRGN.
094 *         final var factory = RandomGeneratorFactory.of("L128X1024MixRandom");
095 *         RandomRegistry.random(factory);
096 *
097 *         final Engine<DoubleGene, Double> engine = null; // @replace substring='null' replacement="..."
098 *         final EvolutionResult<DoubleGene, Double> result = engine.stream()
099 *             .limit(100)
100 *             .collect(toBestEvolutionResult());
101 *     }
102 * }
103 * }
104 *
105 * <h2>Setup of a <i>local</i> PRNG</h2>
106 *
107 * You can temporarily (and locally) change the implementation of the PRNG. E.g.,
108 * for initialize the engine stream with the same initial population.
109 * {@snippet lang="java":
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 = null; // @replace substring='null' replacement="..."
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 * }
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 8.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         * {@snippet lang="java":
239         * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..."
240         * using(new Random(123), r -> {
241         *     seq.shuffle();
242         * });
243         * }
244         *
245         * The example above shuffles the given integer {@code seq} <i>using</i> the
246         * given {@code Random(123)} engine.
247         *
248         * @since 3.0
249         *
250         * @param random the PRNG used within the consumer
251         * @param consumer the consumer which is executed with the <i>scope</i> of
252         *        the given {@code random} engine.
253         * @param <R> the type of the random engine
254         * @throws NullPointerException if one of the arguments is {@code null}
255         */
256        public static <R extends RandomGenerator> void using(
257                final R random,
258                final Consumer<? super R> consumer
259        ) {
260                CONTEXT.with(
261                        () -> random,
262                        r -> { consumer.accept(random); return null; }
263                );
264        }
265
266        /**
267         * Executes the consumer code using the given {@code random} generator.
268         * {@snippet lang="java":
269         * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..."
270         * using(RandomGeneratorFactory.getDefault(), r -> {
271         *     seq.shuffle();
272         * });
273         * }
274         *
275         * The example above shuffles the given integer {@code seq} <i>using</i> the
276         * given {@link RandomGeneratorFactory#getDefault()} factory.
277         *
278         * @since 7.0
279         *
280         * @param factory the random generator factory used within the consumer
281         * @param consumer the consumer which is executed within the <i>scope</i> of
282         *        the given random generator.
283         * @param <R> the type of the random engine
284         * @throws NullPointerException if one of the arguments is {@code null}
285         */
286        public static <R extends RandomGenerator> void using(
287                final RandomGeneratorFactory<? extends R> factory,
288                final Consumer<? super R> consumer
289        ) {
290                CONTEXT.with(
291                        new TLR<>(factory::create),
292                        r -> { consumer.accept(r.get()); return null; }
293                );
294        }
295
296        /**
297         * Executes the consumer code using the given {@code random} generator
298         * supplier.
299         * {@snippet lang="java":
300         * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..."
301         * using(() -> new MyRandomGenerator(), r -> seq.shuffle());
302         * }
303         *
304         * @since 7.0
305         *
306         * @param supplier the random generator supplier used within the consumer
307         * @param consumer the consumer which is executed within the <i>scope</i> of
308         *        the given random generator.
309         * @param <R> the type of the random engine
310         * @throws NullPointerException if one of the arguments is {@code null}
311         */
312        public static <R extends RandomGenerator> void using(
313                final Supplier<? extends R> supplier,
314                final Consumer<? super R> consumer
315        ) {
316                CONTEXT.with(
317                        new TLR<>(supplier),
318                        r -> { consumer.accept(r.get()); return null; }
319                );
320        }
321
322        /**
323         * Opens a new <em>scope</em> with the given random generator and executes
324         * the given function within it. The following example shows how to create a
325         * reproducible list of genotypes:
326         * {@snippet lang="java":
327         * final List<Genotype<DoubleGene>> genotypes =
328         *     with(new LCG64ShiftRandom(123), r ->
329         *         Genotype.of(DoubleChromosome.of(0, 10)).instances()
330         *            .limit(50)
331         *            .collect(toList())
332         *     );
333         * }
334         *
335         * @since 3.0
336         *
337         * @param <R> the type of the random engine
338         * @param <T> the function return type
339         * @param random the PRNG used for the opened scope
340         * @param function the function to apply within the random scope
341         * @return the object returned by the given function
342         * @throws NullPointerException if one of the arguments is {@code null}
343         */
344        public static <R extends RandomGenerator, T> T with(
345                final R random,
346                final Function<? super R, ? extends T> function
347        ) {
348                return CONTEXT.with(
349                        () -> random,
350                        s -> function.apply(random)
351                );
352        }
353
354        /**
355         * Opens a new <em>scope</em> with the given random generator factory and
356         * executes the given function within it.
357         * {@snippet lang="java":
358         * final List<Genotype<DoubleGene>> genotypes =
359         *     with(RandomGeneratorFactory.getDefault(), random ->
360         *         Genotype.of(DoubleChromosome.of(0, 10)).instances()
361         *            .limit(50)
362         *            .collect(toList())
363         *     );
364         * }
365         *
366         * @since 3.0
367         *
368         * @param <R> the type of the random engine
369         * @param <T> the function return type
370         * @param factory the PRNG used for the opened scope
371         * @param function the function to apply within the random scope
372         * @return the object returned by the given function
373         * @throws NullPointerException if one of the arguments is {@code null}.
374         */
375        public static <R extends RandomGenerator, T> T with(
376                final RandomGeneratorFactory<? extends R> factory,
377                final Function<? super R, ? extends T> function
378        ) {
379                return CONTEXT.with(
380                        new TLR<>(factory::create),
381                        r -> function.apply(r.get())
382                );
383        }
384
385        /**
386         * Opens a new <em>scope</em> with the given random generator supplier and
387         * executes the given function within it.
388         * {@snippet lang="java":
389         * final List<Genotype<DoubleGene>> genotypes =
390         *     with(() -> new MyRandomGenerator(), random ->
391         *         Genotype.of(DoubleChromosome.of(0, 10)).instances()
392         *            .limit(50)
393         *            .collect(toList())
394         *     );
395         * }
396         *
397         * @since 3.0
398         *
399         * @param <R> the type of the random engine
400         * @param <T> the function return type
401         * @param supplier the PRNG used for the opened scope
402         * @param function the function to apply within the random scope
403         * @return the object returned by the given function
404         * @throws NullPointerException if one of the arguments is {@code null}.
405         */
406        public static <R extends RandomGenerator, T> T with(
407                final Supplier<? extends R> supplier,
408                final Function<? super R, ? extends T> function
409        ) {
410                return CONTEXT.with(
411                        new TLR<>(supplier),
412                        r -> function.apply(r.get())
413                );
414        }
415
416        @SuppressWarnings("removal")
417        private static final class Env {
418
419                private static final String defaultRandomGenerator =
420                        java.security.AccessController.doPrivileged(
421                                (java.security.PrivilegedAction<String>)Env::get
422                        );
423
424                private static String get() {
425                        return getConfigured()
426                                .or(Env::getDefault)
427                                .orElseGet(Env::getBest);
428                }
429
430                private static Optional<String> getConfigured() {
431                        return Optional.ofNullable(
432                                System.getProperty("io.jenetics.util.defaultRandomGenerator")
433                        );
434                }
435
436                private static Optional<String> getDefault() {
437                        return RandomGeneratorFactory.all()
438                                .map(RandomGeneratorFactory::name)
439                                .filter("L64X256MixRandom"::equals)
440                                .findFirst();
441                }
442
443                private static String getBest() {
444                        final var highestStateBits = Comparator
445                                .<RandomGeneratorFactory<?>>comparingInt(RandomGeneratorFactory::stateBits)
446                                .reversed();
447
448                        return RandomGeneratorFactory.all()
449                                .sorted(highestStateBits)
450                                .map(RandomGeneratorFactory::name)
451                                .findFirst()
452                                .orElse("Random");
453                }
454
455        }
456
457}