001/* 002 * Java Genetic Algorithm Library (jenetics-7.1.0). 003 * Copyright (c) 2007-2022 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.Random; 025import java.util.function.Consumer; 026import java.util.function.Function; 027import java.util.function.Supplier; 028import java.util.random.RandomGenerator; 029import java.util.random.RandomGeneratorFactory; 030 031/** 032 * This class holds the {@link RandomGenerator} engine used for the GA. The 033 * {@code RandomRegistry} is thread safe and is initialized with the 034 * {@link RandomGeneratorFactory#getDefault()} PRNG. 035 * 036 * <h2>Setup the PRNG used for the evolution process</h2> 037 * There are several ways on how to set the {@link RandomGenerator} used during 038 * the evolution process. 039 * <p> 040 * 041 * <b>Using a {@link RandomGeneratorFactory}</b><br> 042 * The following example registers the <em>L128X1024MixRandom</em> random 043 * generator. By using a factory, each threads gets its own generator instance, 044 * which ensures thread-safety without the necessity of the created random 045 * generator to be thread-safe. 046 * <pre>{@code 047 * // This is the default setup. 048 * RandomRegistry.random(RandomGeneratorFactory.getDefault()); 049 * 050 * // Using the "L128X1024MixRandom" random generator for the evolution. 051 * RandomRegistry.random(RandomGeneratorFactory.of("L128X1024MixRandom")); 052 * }</pre> 053 * <br> 054 * 055 * <b>Using a {@link RandomGenerator} {@link Supplier}</b><br> 056 * If you have a random engine, which is not available as 057 * {@link RandomGeneratorFactory}, it is also possible to register a 058 * {@link Supplier} of the desired random generator. This method has the same 059 * thread-safety property as the method above. 060 * <pre>{@code 061 * RandomRegistry.random(() -> new MySpecialRandomGenerator()); 062 * }</pre> 063 * 064 * Register a random generator supplier is also more flexible. It allows to 065 * use the streaming and splitting capabilities of the random generators 066 * implemented in the Java library. 067 * <pre>{@code 068 * final Iterator<RandomGenerator> randoms = 069 * StreamableGenerator.of("L128X1024MixRandom") 070 * .rngs() 071 * .iterator(); 072 * 073 * RandomRegistry.random(randoms::next); 074 * }</pre> 075 * <br> 076 * 077 * <b>Using a {@link RandomGenerator} instance</b><br> 078 * It is also possible to set a single random generator instance for the whole 079 * evolution process. When using this setup, the used random generator must be 080 * thread safe. 081 * <pre>{@code 082 * RandomRegistry.random(new Random(123456)); 083 * }</pre> 084 * <p> 085 * 086 * The following code snippet shows an almost complete example of a typical 087 * random generator setup. 088 * <pre>{@code 089 * public class GA { 090 * public static void main(final String[] args) { 091 * // Initialize the registry with the factory of the PRGN. 092 * final var factory = RandomGeneratorFactory.of("L128X1024MixRandom"); 093 * RandomRegistry.random(factory); 094 * 095 * final Engine<DoubleGene, Double> engine = ...; 096 * final EvolutionResult<DoubleGene, Double> result = engine.stream() 097 * .limit(100) 098 * .collect(toBestEvolutionResult()); 099 * } 100 * } 101 * }</pre> 102 * 103 * <h2>Setup of a <i>local</i> PRNG</h2> 104 * 105 * You can temporarily (and locally) change the implementation of the PRNG. E.g. 106 * for initialize the engine stream with the same initial population. 107 * 108 * <pre>{@code 109 * public class GA { 110 * public static void main(final String[] args) { 111 * // Create a reproducible list of genotypes. 112 * final var factory = RandomGeneratorFactory.of("L128X1024MixRandom"); 113 * final List<Genotype<DoubleGene>> genotypes = 114 * with(factory.create(123), r -> 115 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 116 * .limit(50) 117 * .collect(toList()) 118 * ); 119 * 120 * final Engine<DoubleGene, Double> engine = ...; 121 * final EvolutionResult<DoubleGene, Double> result = engine 122 * // Initialize the evolution stream with the given genotypes. 123 * .stream(genotypes) 124 * .limit(100) 125 * .collect(toBestEvolutionResult()); 126 * } 127 * } 128 * }</pre> 129 * 130 * @see RandomGenerator 131 * @see RandomGeneratorFactory 132 * 133 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 134 * @since 1.0 135 * @version 7.0 136 */ 137public final class RandomRegistry { 138 private RandomRegistry() {} 139 140 /** 141 * Thread local wrapper for a random generator supplier (factory). 142 * 143 * @param <R> the type of the random generator 144 */ 145 private static final class TLR<R extends RandomGenerator> 146 extends ThreadLocal<R> 147 implements Supplier<R> 148 { 149 private final Supplier<? extends R> _factory; 150 151 TLR(final Supplier<? extends R> factory) { 152 _factory = requireNonNull(factory); 153 } 154 155 @Override 156 protected synchronized R initialValue() { 157 return _factory.get(); 158 } 159 } 160 161 private static final TLR<RandomGenerator> DEFAULT_RANDOM_FACTORY = 162 new TLR<>(RandomGeneratorFactory.of("L64X256MixRandom")::create); 163 164 private static final Context<Supplier<? extends RandomGenerator>> CONTEXT = 165 new Context<>(DEFAULT_RANDOM_FACTORY); 166 167 /** 168 * Return the {@link RandomGenerator} of the current scope. 169 * 170 * @return the {@link RandomGenerator} of the current scope 171 */ 172 public static RandomGenerator random() { 173 return CONTEXT.get().get(); 174 } 175 176 /** 177 * Set a new {@link RandomGenerator} for the <em>global</em> scope. The given 178 * {@link RandomGenerator} <b>must</b> be thread safe, which is the case for 179 * the Java {@link Random} class. 180 * 181 * @see #random(RandomGeneratorFactory) 182 * 183 * @param random the new {@link RandomGenerator} for the <em>global</em> 184 * scope 185 * @throws NullPointerException if the {@code random} object is {@code null} 186 */ 187 public static void random(final RandomGenerator random) { 188 requireNonNull(random); 189 CONTEXT.set(() -> random); 190 } 191 192 /** 193 * Set a new {@link RandomGeneratorFactory} for the <em>global</em> scope. 194 * 195 * @param factory the random generator factory 196 * @throws NullPointerException if the {@code factory} object is {@code null}. 197 */ 198 public static <R extends RandomGenerator> void 199 random(final RandomGeneratorFactory<? extends R> factory) { 200 requireNonNull(factory); 201 CONTEXT.set(new TLR<>(factory::create)); 202 } 203 204 /** 205 * Set a new {@link Supplier} of {@link RandomGenerator} for the 206 * <em>global</em> scope. 207 * 208 * @param supplier the random generator supplier 209 * @throws NullPointerException if the {@code supplier} object is {@code null}. 210 */ 211 public static <R extends RandomGenerator> void 212 random(final Supplier<? extends R> supplier) { 213 requireNonNull(supplier); 214 CONTEXT.set(new TLR<>(supplier)); 215 } 216 217 /** 218 * Set the random object to its default value. 219 */ 220 public static void reset() { 221 CONTEXT.reset(); 222 } 223 224 /** 225 * Executes the consumer code using the given {@code random} generator. 226 * 227 * <pre>{@code 228 * final MSeq<Integer> seq = ... 229 * using(new Random(123), r -> { 230 * seq.shuffle(); 231 * }); 232 * }</pre> 233 * 234 * The example above shuffles the given integer {@code seq} <i>using</i> the 235 * given {@code Random(123)} engine. 236 * 237 * @since 3.0 238 * 239 * @param random the PRNG used within the consumer 240 * @param consumer the consumer which is executed with the <i>scope</i> of 241 * the given {@code random} engine. 242 * @param <R> the type of the random engine 243 * @throws NullPointerException if one of the arguments is {@code null} 244 */ 245 public static <R extends RandomGenerator> void using( 246 final R random, 247 final Consumer<? super R> consumer 248 ) { 249 CONTEXT.with( 250 () -> random, 251 r -> { consumer.accept(random); return null; } 252 ); 253 } 254 255 /** 256 * Executes the consumer code using the given {@code random} generator. 257 * 258 * <pre>{@code 259 * final MSeq<Integer> seq = ... 260 * using(RandomGeneratorFactory.getDefault(), r -> { 261 * seq.shuffle(); 262 * }); 263 * }</pre> 264 * 265 * The example above shuffles the given integer {@code seq} <i>using</i> the 266 * given {@link RandomGeneratorFactory#getDefault()} factory. 267 * 268 * @since 7.0 269 * 270 * @param factory the random generator factory used within the consumer 271 * @param consumer the consumer which is executed within the <i>scope</i> of 272 * the given random generator. 273 * @param <R> the type of the random engine 274 * @throws NullPointerException if one of the arguments is {@code null} 275 */ 276 public static <R extends RandomGenerator> void using( 277 final RandomGeneratorFactory<? extends R> factory, 278 final Consumer<? super R> consumer 279 ) { 280 CONTEXT.with( 281 new TLR<>(factory::create), 282 r -> { consumer.accept(r.get()); return null; } 283 ); 284 } 285 286 /** 287 * Executes the consumer code using the given {@code random} generator 288 * supplier. 289 * 290 * <pre>{@code 291 * final MSeq<Integer> seq = ... 292 * using(() -> new MyRandomGenerator(), r -> { 293 * seq.shuffle(); 294 * }); 295 * }</pre> 296 * 297 * @since 7.0 298 * 299 * @param supplier the random generator supplier used within the consumer 300 * @param consumer the consumer which is executed within the <i>scope</i> of 301 * the given random generator. 302 * @param <R> the type of the random engine 303 * @throws NullPointerException if one of the arguments is {@code null} 304 */ 305 public static <R extends RandomGenerator> void using( 306 final Supplier<? extends R> supplier, 307 final Consumer<? super R> consumer 308 ) { 309 CONTEXT.with( 310 new TLR<>(supplier), 311 r -> { consumer.accept(r.get()); return null; } 312 ); 313 } 314 315 /** 316 * Opens a new <em>scope</em> with the given random generator and executes 317 * the given function within it. The following example shows how to create a 318 * reproducible list of genotypes: 319 * <pre>{@code 320 * final List<Genotype<DoubleGene>> genotypes = 321 * with(new LCG64ShiftRandom(123), r -> 322 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 323 * .limit(50) 324 * .collect(toList()) 325 * ); 326 * }</pre> 327 * 328 * @since 3.0 329 * 330 * @param <R> the type of the random engine 331 * @param <T> the function return type 332 * @param random the PRNG used for the opened scope 333 * @param function the function to apply within the random scope 334 * @return the object returned by the given function 335 * @throws NullPointerException if one of the arguments is {@code null} 336 */ 337 public static <R extends RandomGenerator, T> T with( 338 final R random, 339 final Function<? super R, ? extends T> function 340 ) { 341 return CONTEXT.with( 342 () -> random, 343 s -> function.apply(random) 344 ); 345 } 346 347 /** 348 * Opens a new <em>scope</em> with the given random generator factory and 349 * executes the given function within it. 350 * <pre>{@code 351 * final List<Genotype<DoubleGene>> genotypes = 352 * with(RandomGeneratorFactory.getDefault(), random -> 353 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 354 * .limit(50) 355 * .collect(toList()) 356 * ); 357 * }</pre> 358 * 359 * @since 3.0 360 * 361 * @param <R> the type of the random engine 362 * @param <T> the function return type 363 * @param factory the PRNG used for the opened scope 364 * @param function the function to apply within the random scope 365 * @return the object returned by the given function 366 * @throws NullPointerException if one of the arguments is {@code null}. 367 */ 368 public static <R extends RandomGenerator, T> T with( 369 final RandomGeneratorFactory<? extends R> factory, 370 final Function<? super R, ? extends T> function 371 ) { 372 return CONTEXT.with( 373 new TLR<>(factory::create), 374 r -> function.apply(r.get()) 375 ); 376 } 377 /** 378 * Opens a new <em>scope</em> with the given random generator supplier and 379 * executes the given function within it. 380 * <pre>{@code 381 * final List<Genotype<DoubleGene>> genotypes = 382 * with(() -> new MyRandomGenerator(), random -> 383 * Genotype.of(DoubleChromosome.of(0, 10)).instances() 384 * .limit(50) 385 * .collect(toList()) 386 * ); 387 * }</pre> 388 * 389 * @since 3.0 390 * 391 * @param <R> the type of the random engine 392 * @param <T> the function return type 393 * @param supplier the PRNG used for the opened scope 394 * @param function the function to apply within the random scope 395 * @return the object returned by the given function 396 * @throws NullPointerException if one of the arguments is {@code null}. 397 */ 398 399 public static <R extends RandomGenerator, T> T with( 400 final Supplier<? extends R> supplier, 401 final Function<? super R, ? extends T> function 402 ) { 403 return CONTEXT.with( 404 new TLR<>(supplier), 405 r -> function.apply(r.get()) 406 ); 407 } 408 409}