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}