001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 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.lang.Math.max; 023import static java.util.Objects.requireNonNull; 024 025import java.util.Random; 026import java.util.function.Consumer; 027import java.util.function.Function; 028import java.util.function.Supplier; 029import java.util.random.RandomGenerator; 030import java.util.random.RandomGeneratorFactory; 031 032/** 033 * This class holds the {@link RandomGenerator} engine used for the GA. The 034 * {@code RandomRegistry} is thread safe and is initialized with the 035 * {@link RandomGeneratorFactory#getDefault()} PRNG. 036 * 037 * <h2>Setup the PRNG used for the evolution process</h2> 038 * There are several ways on how to set the {@link RandomGenerator} used during 039 * the evolution process. 040 * <p> 041 * 042 * <b>Using a {@link RandomGeneratorFactory}</b><br> 043 * The following example registers the <em>L128X1024MixRandom</em> random 044 * generator. By using a factory, each thread gets its own generator instance, 045 * which ensures thread-safety without the necessity of the created random 046 * generator to be thread-safe. 047 * <pre>{@code 048 * // This is the default setup. 049 * RandomRegistry.random(RandomGeneratorFactory.getDefault()); 050 * 051 * // Using the "L128X1024MixRandom" random generator for the evolution. 052 * RandomRegistry.random(RandomGeneratorFactory.of("L128X1024MixRandom")); 053 * }</pre> 054 * <br> 055 * 056 * <b>Using a {@link RandomGenerator} {@link Supplier}</b><br> 057 * If you have a random engine, which is not available as 058 * {@link RandomGeneratorFactory}, it is also possible to register a 059 * {@link Supplier} of the desired random generator. This method has the same 060 * thread-safety property as the method above. 061 * <pre>{@code 062 * RandomRegistry.random(() -> new MySpecialRandomGenerator()); 063 * }</pre> 064 * 065 * Register a random generator supplier is also more flexible. It allows 066 * using the streaming and splitting capabilities of the random generators 067 * implemented in the Java library. 068 * <pre>{@code 069 * final Iterator<RandomGenerator> randoms = 070 * StreamableGenerator.of("L128X1024MixRandom") 071 * .rngs() 072 * .iterator(); 073 * 074 * RandomRegistry.random(randoms::next); 075 * }</pre> 076 * <br> 077 * 078 * <b>Using a {@link RandomGenerator} instance</b><br> 079 * It is also possible to set a single random generator instance for the whole 080 * evolution process. When using this setup, the used random generator must be 081 * thread safe. 082 * <pre>{@code 083 * RandomRegistry.random(new Random(123456)); 084 * }</pre> 085 * <p> 086 * 087 * The following code snippet shows an almost complete example of a typical 088 * random generator setup. 089 * <pre>{@code 090 * public class GA { 091 * public static void main(final String[] args) { 092 * // Initialize the registry with the factory of the PRGN. 093 * final var factory = RandomGeneratorFactory.of("L128X1024MixRandom"); 094 * RandomRegistry.random(factory); 095 * 096 * final Engine<DoubleGene, Double> engine = ...; 097 * final EvolutionResult<DoubleGene, Double> result = engine.stream() 098 * .limit(100) 099 * .collect(toBestEvolutionResult()); 100 * } 101 * } 102 * }</pre> 103 * 104 * <h2>Setup of a <i>local</i> PRNG</h2> 105 * 106 * You can temporarily (and locally) change the implementation of the PRNG. E.g., 107 * for initialize the engine stream with the same initial population. 108 * 109 * <pre>{@code 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 = ...; 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 * }</pre> 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 7.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 * 239 * <pre>{@code 240 * final MSeq<Integer> seq = ... 241 * using(new Random(123), r -> { 242 * seq.shuffle(); 243 * }); 244 * }</pre> 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 * 270 * <pre>{@code 271 * final MSeq<Integer> seq = ... 272 * using(RandomGeneratorFactory.getDefault(), r -> { 273 * seq.shuffle(); 274 * }); 275 * }</pre> 276 * 277 * The example above shuffles the given integer {@code seq} <i>using</i> the 278 * given {@link RandomGeneratorFactory#getDefault()} factory. 279 * 280 * @since 7.0 281 * 282 * @param factory the random generator factory used within the consumer 283 * @param consumer the consumer which is executed within the <i>scope</i> of 284 * the given random generator. 285 * @param <R> the type of the random engine 286 * @throws NullPointerException if one of the arguments is {@code null} 287 */ 288 public static <R extends RandomGenerator> void using( 289 final RandomGeneratorFactory<? extends R> factory, 290 final Consumer<? super R> consumer 291 ) { 292 CONTEXT.with( 293 new TLR<>(factory::create), 294 r -> { consumer.accept(r.get()); return null; } 295 ); 296 } 297 298 /** 299 * Executes the consumer code using the given {@code random} generator 300 * supplier. 301 * 302 * <pre>{@code 303 * final MSeq<Integer> seq = ... 304 * using(() -> new MyRandomGenerator(), r -> { 305 * seq.shuffle(); 306 * }); 307 * }</pre> 308 * 309 * @since 7.0 310 * 311 * @param supplier the random generator supplier used within the consumer 312 * @param consumer the consumer which is executed within the <i>scope</i> of 313 * the given random generator. 314 * @param <R> the type of the random engine 315 * @throws NullPointerException if one of the arguments is {@code null} 316 */ 317 public static <R extends RandomGenerator> void using( 318 final Supplier<? extends R> supplier, 319 final Consumer<? super R> consumer 320 ) { 321 CONTEXT.with( 322 new TLR<>(supplier), 323 r -> { consumer.accept(r.get()); return null; } 324 ); 325 } 326 327 /** 328 * Opens a new <em>scope</em> with the given random generator and executes 329 * the given function within it. The following example shows how to create a 330 * reproducible list of genotypes: 331 * <pre>{@code 332 * final List<Genotype<DoubleGene>> genotypes = 333 * with(new LCG64ShiftRandom(123), r -> 334 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 335 * .limit(50) 336 * .collect(toList()) 337 * ); 338 * }</pre> 339 * 340 * @since 3.0 341 * 342 * @param <R> the type of the random engine 343 * @param <T> the function return type 344 * @param random the PRNG used for the opened scope 345 * @param function the function to apply within the random scope 346 * @return the object returned by the given function 347 * @throws NullPointerException if one of the arguments is {@code null} 348 */ 349 public static <R extends RandomGenerator, T> T with( 350 final R random, 351 final Function<? super R, ? extends T> function 352 ) { 353 return CONTEXT.with( 354 () -> random, 355 s -> function.apply(random) 356 ); 357 } 358 359 /** 360 * Opens a new <em>scope</em> with the given random generator factory and 361 * executes the given function within it. 362 * <pre>{@code 363 * final List<Genotype<DoubleGene>> genotypes = 364 * with(RandomGeneratorFactory.getDefault(), random -> 365 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 366 * .limit(50) 367 * .collect(toList()) 368 * ); 369 * }</pre> 370 * 371 * @since 3.0 372 * 373 * @param <R> the type of the random engine 374 * @param <T> the function return type 375 * @param factory the PRNG used for the opened scope 376 * @param function the function to apply within the random scope 377 * @return the object returned by the given function 378 * @throws NullPointerException if one of the arguments is {@code null}. 379 */ 380 public static <R extends RandomGenerator, T> T with( 381 final RandomGeneratorFactory<? extends R> factory, 382 final Function<? super R, ? extends T> function 383 ) { 384 return CONTEXT.with( 385 new TLR<>(factory::create), 386 r -> function.apply(r.get()) 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 * <pre>{@code 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 * }</pre> 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 411 public static <R extends RandomGenerator, T> T with( 412 final Supplier<? extends R> supplier, 413 final Function<? super R, ? extends T> function 414 ) { 415 return CONTEXT.with( 416 new TLR<>(supplier), 417 r -> function.apply(r.get()) 418 ); 419 } 420 421 @SuppressWarnings("removal") 422 private static final class Env { 423 private static final String defaultRandomGenerator = 424 java.security.AccessController.doPrivileged( 425 (java.security.PrivilegedAction<String>)() -> 426 System.getProperty( 427 "io.jenetics.util.defaultRandomGenerator", 428 "L64X256MixRandom" 429 ) 430 ); 431 } 432 433}