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 }
|