001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.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 *
110 * {@snippet lang="java":
111 * public class GA {
112 *     public static void main(final String[] args) {
113 *         // Create a reproducible list of genotypes.
114 *         final var factory = RandomGeneratorFactory.of("L128X1024MixRandom");
115 *         final List<Genotype<DoubleGene>> genotypes =
116 *             with(factory.create(123), r ->
117 *                 Genotype.of(DoubleChromosome.of(0, 10)).instances()
118 *                     .limit(50)
119 *                     .collect(toList())
120 *             );
121 *
122 *         final Engine<DoubleGene, Double> engine = null; // @replace substring='null' replacement="..."
123 *         final EvolutionResult<DoubleGene, Double> result = engine
124 *              // Initialize the evolution stream with the given genotypes.
125 *             .stream(genotypes)
126 *             .limit(100)
127 *             .collect(toBestEvolutionResult());
128 *     }
129 * }
130 * }
131 *
132 * <p>
133 * The default random generator used by <em>Jenetics</em> is
134 * {@code L64X256MixRandom}. Via the system property
135 * {@code io.jenetics.util.defaultRandomGenerator}, it is possible to use a
136 * different random generator.
137 * <pre>{@code
138 * java -Dio.jenetics.util.defaultRandomGenerator=L64X1024MixRandom \
139 *      -cp jenetics-@__version__@.jar:app.jar \
140 *          com.foo.bar.MyJeneticsApp
141 * }</pre>
142 *
143 * @see RandomGenerator
144 * @see RandomGeneratorFactory
145 *
146 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
147 * @since 1.0
148 * @version 8.0
149 */
150public final class RandomRegistry {
151        private RandomRegistry() {}
152
153        /**
154         * Thread local wrapper for a random generator supplier (factory).
155         *
156         * @param <R> the type of the random generator
157         */
158        private static final class TLR<R extends RandomGenerator>
159                extends ThreadLocal<R>
160                implements Supplier<R>
161        {
162                private final Supplier<? extends R> _factory;
163
164                TLR(final Supplier<? extends R> factory) {
165                        _factory = requireNonNull(factory);
166                }
167
168                @Override
169                protected synchronized R initialValue() {
170                        return _factory.get();
171                }
172        }
173
174        private static final TLR<RandomGenerator> DEFAULT_RANDOM_FACTORY =
175                new TLR<>(RandomGeneratorFactory.of(Env.defaultRandomGenerator)::create);
176
177        private static final Context<Supplier<? extends RandomGenerator>> CONTEXT =
178                new Context<>(DEFAULT_RANDOM_FACTORY);
179
180        /**
181         * Return the {@link RandomGenerator} of the current scope.
182         *
183         * @return the {@link RandomGenerator} of the current scope
184         */
185        public static RandomGenerator random() {
186                return CONTEXT.get().get();
187        }
188
189        /**
190         * Set a new {@link RandomGenerator} for the <em>global</em> scope. The given
191         * {@link RandomGenerator} <b>must</b> be thread safe, which is the case for
192         * the Java {@link Random} class.
193         *
194         * @see #random(RandomGeneratorFactory)
195         *
196         * @param random the new {@link RandomGenerator} for the <em>global</em>
197         *        scope
198         * @throws NullPointerException if the {@code random} object is {@code null}
199         */
200        public static void random(final RandomGenerator random) {
201                requireNonNull(random);
202                CONTEXT.set(() -> random);
203        }
204
205        /**
206         * Set a new {@link RandomGeneratorFactory} for the <em>global</em> scope.
207         *
208         * @param factory the random generator factory
209         * @throws NullPointerException if the {@code factory} object is {@code null}.
210         */
211        public static <R extends RandomGenerator> void
212        random(final RandomGeneratorFactory<? extends R> factory) {
213                requireNonNull(factory);
214                CONTEXT.set(new TLR<>(factory::create));
215        }
216
217        /**
218         * Set a new {@link Supplier} of {@link RandomGenerator} for the
219         * <em>global</em> scope.
220         *
221         * @param supplier the random generator supplier
222         * @throws NullPointerException if the {@code supplier} object is {@code null}.
223         */
224        public static <R extends RandomGenerator> void
225        random(final Supplier<? extends R> supplier) {
226                requireNonNull(supplier);
227                CONTEXT.set(new TLR<>(supplier));
228        }
229
230        /**
231         * Set the random object to its default value.
232         */
233        public static void reset() {
234                CONTEXT.reset();
235        }
236
237        /**
238         * Executes the consumer code using the given {@code random} generator.
239         *
240         * {@snippet lang="java":
241         * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..."
242         * using(new Random(123), r -> {
243         *     seq.shuffle();
244         * });
245         * }
246         *
247         * The example above shuffles the given integer {@code seq} <i>using</i> the
248         * given {@code Random(123)} engine.
249         *
250         * @since 3.0
251         *
252         * @param random the PRNG used within the consumer
253         * @param consumer the consumer which is executed with the <i>scope</i> of
254         *        the given {@code random} engine.
255         * @param <R> the type of the random engine
256         * @throws NullPointerException if one of the arguments is {@code null}
257         */
258        public static <R extends RandomGenerator> void using(
259                final R random,
260                final Consumer<? super R> consumer
261        ) {
262                CONTEXT.with(
263                        () -> random,
264                        r -> { consumer.accept(random); return null; }
265                );
266        }
267
268        /**
269         * Executes the consumer code using the given {@code random} generator.
270         *
271         * {@snippet lang="java":
272         * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..."
273         * using(RandomGeneratorFactory.getDefault(), r -> {
274         *     seq.shuffle();
275         * });
276         * }
277         *
278         * The example above shuffles the given integer {@code seq} <i>using</i> the
279         * given {@link RandomGeneratorFactory#getDefault()} factory.
280         *
281         * @since 7.0
282         *
283         * @param factory the random generator factory used within the consumer
284         * @param consumer the consumer which is executed within the <i>scope</i> of
285         *        the given random generator.
286         * @param <R> the type of the random engine
287         * @throws NullPointerException if one of the arguments is {@code null}
288         */
289        public static <R extends RandomGenerator> void using(
290                final RandomGeneratorFactory<? extends R> factory,
291                final Consumer<? super R> consumer
292        ) {
293                CONTEXT.with(
294                        new TLR<>(factory::create),
295                        r -> { consumer.accept(r.get()); return null; }
296                );
297        }
298
299        /**
300         * Executes the consumer code using the given {@code random} generator
301         * supplier.
302         *
303         * {@snippet lang="java":
304         * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..."
305         * using(() -> new MyRandomGenerator(), r -> seq.shuffle());
306         * }
307         *
308         * @since 7.0
309         *
310         * @param supplier the random generator supplier used within the consumer
311         * @param consumer the consumer which is executed within the <i>scope</i> of
312         *        the given random generator.
313         * @param <R> the type of the random engine
314         * @throws NullPointerException if one of the arguments is {@code null}
315         */
316        public static <R extends RandomGenerator> void using(
317                final Supplier<? extends R> supplier,
318                final Consumer<? super R> consumer
319        ) {
320                CONTEXT.with(
321                        new TLR<>(supplier),
322                        r -> { consumer.accept(r.get()); return null; }
323                );
324        }
325
326        /**
327         * Opens a new <em>scope</em> with the given random generator and executes
328         * the given function within it. The following example shows how to create a
329         * reproducible list of genotypes:
330         * {@snippet lang="java":
331         * final List<Genotype<DoubleGene>> genotypes =
332         *     with(new LCG64ShiftRandom(123), r ->
333         *         Genotype.of(DoubleChromosome.of(0, 10)).instances()
334         *            .limit(50)
335         *            .collect(toList())
336         *     );
337         * }
338         *
339         * @since 3.0
340         *
341         * @param <R> the type of the random engine
342         * @param <T> the function return type
343         * @param random the PRNG used for the opened scope
344         * @param function the function to apply within the random scope
345         * @return the object returned by the given function
346         * @throws NullPointerException if one of the arguments is {@code null}
347         */
348        public static <R extends RandomGenerator, T> T with(
349                final R random,
350                final Function<? super R, ? extends T> function
351        ) {
352                return CONTEXT.with(
353                        () -> random,
354                        s -> function.apply(random)
355                );
356        }
357
358        /**
359         * Opens a new <em>scope</em> with the given random generator factory and
360         * executes the given function within it.
361         * {@snippet lang="java":
362         * final List<Genotype<DoubleGene>> genotypes =
363         *     with(RandomGeneratorFactory.getDefault(), random ->
364         *         Genotype.of(DoubleChromosome.of(0, 10)).instances()
365         *            .limit(50)
366         *            .collect(toList())
367         *     );
368         * }
369         *
370         * @since 3.0
371         *
372         * @param <R> the type of the random engine
373         * @param <T> the function return type
374         * @param factory the PRNG used for the opened scope
375         * @param function the function to apply within the random scope
376         * @return the object returned by the given function
377         * @throws NullPointerException if one of the arguments is {@code null}.
378         */
379        public static <R extends RandomGenerator, T> T with(
380                final RandomGeneratorFactory<? extends R> factory,
381                final Function<? super R, ? extends T> function
382        ) {
383                return CONTEXT.with(
384                        new TLR<>(factory::create),
385                        r -> function.apply(r.get())
386                );
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         * {@snippet lang="java":
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         * }
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        public static <R extends RandomGenerator, T> T with(
411                final Supplier<? extends R> supplier,
412                final Function<? super R, ? extends T> function
413        ) {
414                return CONTEXT.with(
415                        new TLR<>(supplier),
416                        r -> function.apply(r.get())
417                );
418        }
419
420        @SuppressWarnings("removal")
421        private static final class Env {
422
423                private static final String defaultRandomGenerator =
424                        java.security.AccessController.doPrivileged(
425                                (java.security.PrivilegedAction<String>)Env::get
426                        );
427
428                private static String get() {
429                        return getConfigured()
430                                .or(Env::getDefault)
431                                .orElseGet(Env::getBest);
432                }
433
434                private static Optional<String> getConfigured() {
435                        return Optional.ofNullable(
436                                System.getProperty("io.jenetics.util.defaultRandomGenerator")
437                        );
438                }
439
440                private static Optional<String> getDefault() {
441                        return RandomGeneratorFactory.all()
442                                .map(RandomGeneratorFactory::name)
443                                .filter("L64X256MixRandom"::equals)
444                                .findFirst();
445                }
446
447                private static String getBest() {
448                        final var highestStateBits = Comparator
449                                .<RandomGeneratorFactory<?>>comparingInt(RandomGeneratorFactory::stateBits)
450                                .reversed();
451
452                        return RandomGeneratorFactory.all()
453                                .sorted(highestStateBits)
454                                .map(RandomGeneratorFactory::name)
455                                .findFirst()
456                                .orElse("Random");
457                }
458
459        }
460
461}