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}