001/* 002 * Java Genetic Algorithm Library (jenetics-8.3.0). 003 * Copyright (c) 2007-2025 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>Set up 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 /** 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> 175 DEFAULT_RANDOM_FACTORY = 176 new TLR<>(RandomGeneratorFactory.of(Env.defaultRandomGenerator)::create); 177 178 private static final Context<Supplier<? extends RandomGenerator>> 179 CONTEXT = 180 new Context<>(DEFAULT_RANDOM_FACTORY); 181 182 /** 183 * Return the {@link RandomGenerator} of the current scope. 184 * 185 * @return the {@link RandomGenerator} of the current scope 186 */ 187 public static RandomGenerator random() { 188 return CONTEXT.get().get(); 189 } 190 191 /** 192 * Set a new {@link RandomGenerator} for the <em>global</em> scope. The given 193 * {@link RandomGenerator} <b>must</b> be thread safe, which is the case for 194 * the Java {@link Random} class. 195 * 196 * @see #random(RandomGeneratorFactory) 197 * 198 * @param random the new {@link RandomGenerator} for the <em>global</em> 199 * scope 200 * @throws NullPointerException if the {@code random} object is {@code null} 201 */ 202 public static void random(final RandomGenerator random) { 203 requireNonNull(random); 204 CONTEXT.set(() -> random); 205 } 206 207 /** 208 * Set a new {@link RandomGeneratorFactory} for the <em>global</em> scope. 209 * 210 * @param factory the random generator factory 211 * @throws NullPointerException if the {@code factory} object is {@code null}. 212 */ 213 public static <R extends RandomGenerator> void 214 random(final RandomGeneratorFactory<? extends R> factory) { 215 requireNonNull(factory); 216 CONTEXT.set(new TLR<>(factory::create)); 217 } 218 219 /** 220 * Set a new {@link Supplier} of {@link RandomGenerator} for the 221 * <em>global</em> scope. 222 * 223 * @param supplier the random generator supplier 224 * @throws NullPointerException if the {@code supplier} object is {@code null}. 225 */ 226 public static <R extends RandomGenerator> void 227 random(final Supplier<? extends R> supplier) { 228 requireNonNull(supplier); 229 CONTEXT.set(new TLR<>(supplier)); 230 } 231 232 /** 233 * Set the random object to its default value. 234 */ 235 public static void reset() { 236 CONTEXT.reset(); 237 } 238 239 /** 240 * Executes the consumer code using the given {@code random} generator. 241 * {@snippet lang="java": 242 * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..." 243 * using(new Random(123), r -> seq.shuffle()); 244 * } 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 * {@snippet lang="java": 270 * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..." 271 * using(RandomGeneratorFactory.getDefault(), r -> seq.shuffle()); 272 * } 273 * 274 * The example above shuffles the given integer {@code seq} <i>using</i> the 275 * given {@link RandomGeneratorFactory#getDefault()} factory. 276 * 277 * @since 7.0 278 * 279 * @param factory the random generator factory used within the consumer 280 * @param consumer the consumer which is executed within the <i>scope</i> of 281 * the given random generator. 282 * @param <R> the type of the random engine 283 * @throws NullPointerException if one of the arguments is {@code null} 284 */ 285 public static <R extends RandomGenerator> void using( 286 final RandomGeneratorFactory<? extends R> factory, 287 final Consumer<? super R> consumer 288 ) { 289 CONTEXT.with( 290 new TLR<>(factory::create), 291 r -> { consumer.accept(r.get()); return null; } 292 ); 293 } 294 295 /** 296 * Executes the consumer code using the given {@code random} generator 297 * supplier. 298 * {@snippet lang="java": 299 * final MSeq<Integer> seq = null; // @replace substring='null' replacement="..." 300 * using(() -> new MyRandomGenerator(), r -> seq.shuffle()); 301 * } 302 * 303 * @since 7.0 304 * 305 * @param supplier the random generator supplier used within the consumer 306 * @param consumer the consumer which is executed within the <i>scope</i> of 307 * the given random generator. 308 * @param <R> the type of the random engine 309 * @throws NullPointerException if one of the arguments is {@code null} 310 */ 311 public static <R extends RandomGenerator> void using( 312 final Supplier<? extends R> supplier, 313 final Consumer<? super R> consumer 314 ) { 315 CONTEXT.with( 316 new TLR<>(supplier), 317 r -> { consumer.accept(r.get()); return null; } 318 ); 319 } 320 321 /** 322 * Opens a new <em>scope</em> with the given random generator and executes 323 * the given function within it. The following example shows how to create a 324 * reproducible list of genotypes: 325 * {@snippet lang="java": 326 * final List<Genotype<DoubleGene>> genotypes = 327 * with(new LCG64ShiftRandom(123), r -> 328 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 329 * .limit(50) 330 * .collect(toList()) 331 * ); 332 * } 333 * 334 * @since 3.0 335 * 336 * @param <R> the type of the random engine 337 * @param <T> the function return type 338 * @param random the PRNG used for the opened scope 339 * @param function the function to apply within the random scope 340 * @return the object returned by the given function 341 * @throws NullPointerException if one of the arguments is {@code null} 342 */ 343 public static <R extends RandomGenerator, T> T with( 344 final R random, 345 final Function<? super R, ? extends T> function 346 ) { 347 return CONTEXT.with( 348 () -> random, 349 s -> function.apply(random) 350 ); 351 } 352 353 /** 354 * Opens a new <em>scope</em> with the given random generator factory and 355 * executes the given function within it. 356 * {@snippet lang="java": 357 * final List<Genotype<DoubleGene>> genotypes = 358 * with(RandomGeneratorFactory.getDefault(), random -> 359 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 360 * .limit(50) 361 * .collect(toList()) 362 * ); 363 * } 364 * 365 * @since 3.0 366 * 367 * @param <R> the type of the random engine 368 * @param <T> the function return type 369 * @param factory the PRNG used for the opened scope 370 * @param function the function to apply within the random scope 371 * @return the object returned by the given function 372 * @throws NullPointerException if one of the arguments is {@code null}. 373 */ 374 public static <R extends RandomGenerator, T> T with( 375 final RandomGeneratorFactory<? extends R> factory, 376 final Function<? super R, ? extends T> function 377 ) { 378 return CONTEXT.with( 379 new TLR<>(factory::create), 380 r -> function.apply(r.get()) 381 ); 382 } 383 384 /** 385 * Opens a new <em>scope</em> with the given random generator supplier and 386 * executes the given function within it. 387 * {@snippet lang="java": 388 * final List<Genotype<DoubleGene>> genotypes = 389 * with(() -> new MyRandomGenerator(), random -> 390 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 391 * .limit(50) 392 * .collect(toList()) 393 * ); 394 * } 395 * 396 * @since 3.0 397 * 398 * @param <R> the type of the random engine 399 * @param <T> the function return type 400 * @param supplier the PRNG used for the opened scope 401 * @param function the function to apply within the random scope 402 * @return the object returned by the given function 403 * @throws NullPointerException if one of the arguments is {@code null}. 404 */ 405 public static <R extends RandomGenerator, T> T with( 406 final Supplier<? extends R> supplier, 407 final Function<? super R, ? extends T> function 408 ) { 409 return CONTEXT.with( 410 new TLR<>(supplier), 411 r -> function.apply(r.get()) 412 ); 413 } 414 415 private static final class Env { 416 417 private static final String defaultRandomGenerator = get(); 418 419 private static String get() { 420 return getConfigured() 421 .or(Env::getDefault) 422 .orElseGet(Env::getBest); 423 } 424 425 private static Optional<String> getConfigured() { 426 return Optional.ofNullable( 427 System.getProperty("io.jenetics.util.defaultRandomGenerator") 428 ); 429 } 430 431 private static Optional<String> getDefault() { 432 return RandomGeneratorFactory.all() 433 .map(RandomGeneratorFactory::name) 434 .filter("L64X256MixRandom"::equals) 435 .findFirst(); 436 } 437 438 private static String getBest() { 439 final var highestStateBits = Comparator 440 .<RandomGeneratorFactory<?>>comparingInt(RandomGeneratorFactory::stateBits) 441 .reversed(); 442 443 return RandomGeneratorFactory.all() 444 .sorted(highestStateBits) 445 .map(RandomGeneratorFactory::name) 446 .findFirst() 447 .orElse("Random"); 448 } 449 450 } 451 452}