RandomRegistry.java
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  */
020 package io.jenetics.util;
021 
022 import static java.util.Objects.requireNonNull;
023 
024 import java.util.Comparator;
025 import java.util.Optional;
026 import java.util.Random;
027 import java.util.function.Consumer;
028 import java.util.function.Function;
029 import java.util.function.Supplier;
030 import java.util.random.RandomGenerator;
031 import 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  */
150 public 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 }