io.jenetics.util.RandomRegistry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jenetics Show documentation
Show all versions of jenetics Show documentation
Jenetics - Java Genetic Algorithm Library
/*
* Java Genetic Algorithm Library (jenetics-7.1.2).
* Copyright (c) 2007-2023 Franz Wilhelmstötter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author:
* Franz Wilhelmstötter ([email protected])
*/
package io.jenetics.util;
import static java.util.Objects.requireNonNull;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
/**
* This class holds the {@link RandomGenerator} engine used for the GA. The
* {@code RandomRegistry} is thread safe and is initialized with the
* {@link RandomGeneratorFactory#getDefault()} PRNG.
*
* Setup the PRNG used for the evolution process
* There are several ways on how to set the {@link RandomGenerator} used during
* the evolution process.
*
*
* Using a {@link RandomGeneratorFactory}
* The following example registers the L128X1024MixRandom random
* generator. By using a factory, each threads gets its own generator instance,
* which ensures thread-safety without the necessity of the created random
* generator to be thread-safe.
*
{@code
* // This is the default setup.
* RandomRegistry.random(RandomGeneratorFactory.getDefault());
*
* // Using the "L128X1024MixRandom" random generator for the evolution.
* RandomRegistry.random(RandomGeneratorFactory.of("L128X1024MixRandom"));
* }
*
*
* Using a {@link RandomGenerator} {@link Supplier}
* If you have a random engine, which is not available as
* {@link RandomGeneratorFactory}, it is also possible to register a
* {@link Supplier} of the desired random generator. This method has the same
* thread-safety property as the method above.
* {@code
* RandomRegistry.random(() -> new MySpecialRandomGenerator());
* }
*
* Register a random generator supplier is also more flexible. It allows to
* use the streaming and splitting capabilities of the random generators
* implemented in the Java library.
* {@code
* final Iterator randoms =
* StreamableGenerator.of("L128X1024MixRandom")
* .rngs()
* .iterator();
*
* RandomRegistry.random(randoms::next);
* }
*
*
* Using a {@link RandomGenerator} instance
* It is also possible to set a single random generator instance for the whole
* evolution process. When using this setup, the used random generator must be
* thread safe.
* {@code
* RandomRegistry.random(new Random(123456));
* }
*
*
* The following code snippet shows an almost complete example of a typical
* random generator setup.
*
{@code
* public class GA {
* public static void main(final String[] args) {
* // Initialize the registry with the factory of the PRGN.
* final var factory = RandomGeneratorFactory.of("L128X1024MixRandom");
* RandomRegistry.random(factory);
*
* final Engine engine = ...;
* final EvolutionResult result = engine.stream()
* .limit(100)
* .collect(toBestEvolutionResult());
* }
* }
* }
*
* Setup of a local PRNG
*
* You can temporarily (and locally) change the implementation of the PRNG. E.g.
* for initialize the engine stream with the same initial population.
*
* {@code
* public class GA {
* public static void main(final String[] args) {
* // Create a reproducible list of genotypes.
* final var factory = RandomGeneratorFactory.of("L128X1024MixRandom");
* final List> genotypes =
* with(factory.create(123), r ->
* Genotype.of(DoubleChromosome.of(0, 10)).instances()
* .limit(50)
* .collect(toList())
* );
*
* final Engine engine = ...;
* final EvolutionResult result = engine
* // Initialize the evolution stream with the given genotypes.
* .stream(genotypes)
* .limit(100)
* .collect(toBestEvolutionResult());
* }
* }
* }
*
* @see RandomGenerator
* @see RandomGeneratorFactory
*
* @author Franz Wilhelmstötter
* @since 1.0
* @version 7.0
*/
public final class RandomRegistry {
private RandomRegistry() {}
/**
* Thread local wrapper for a random generator supplier (factory).
*
* @param the type of the random generator
*/
private static final class TLR
extends ThreadLocal
implements Supplier
{
private final Supplier extends R> _factory;
TLR(final Supplier extends R> factory) {
_factory = requireNonNull(factory);
}
@Override
protected synchronized R initialValue() {
return _factory.get();
}
}
private static final TLR DEFAULT_RANDOM_FACTORY =
new TLR<>(RandomGeneratorFactory.of("L64X256MixRandom")::create);
private static final Context> CONTEXT =
new Context<>(DEFAULT_RANDOM_FACTORY);
/**
* Return the {@link RandomGenerator} of the current scope.
*
* @return the {@link RandomGenerator} of the current scope
*/
public static RandomGenerator random() {
return CONTEXT.get().get();
}
/**
* Set a new {@link RandomGenerator} for the global scope. The given
* {@link RandomGenerator} must be thread safe, which is the case for
* the Java {@link Random} class.
*
* @see #random(RandomGeneratorFactory)
*
* @param random the new {@link RandomGenerator} for the global
* scope
* @throws NullPointerException if the {@code random} object is {@code null}
*/
public static void random(final RandomGenerator random) {
requireNonNull(random);
CONTEXT.set(() -> random);
}
/**
* Set a new {@link RandomGeneratorFactory} for the global scope.
*
* @param factory the random generator factory
* @throws NullPointerException if the {@code factory} object is {@code null}.
*/
public static void
random(final RandomGeneratorFactory extends R> factory) {
requireNonNull(factory);
CONTEXT.set(new TLR<>(factory::create));
}
/**
* Set a new {@link Supplier} of {@link RandomGenerator} for the
* global scope.
*
* @param supplier the random generator supplier
* @throws NullPointerException if the {@code supplier} object is {@code null}.
*/
public static void
random(final Supplier extends R> supplier) {
requireNonNull(supplier);
CONTEXT.set(new TLR<>(supplier));
}
/**
* Set the random object to its default value.
*/
public static void reset() {
CONTEXT.reset();
}
/**
* Executes the consumer code using the given {@code random} generator.
*
* {@code
* final MSeq seq = ...
* using(new Random(123), r -> {
* seq.shuffle();
* });
* }
*
* The example above shuffles the given integer {@code seq} using the
* given {@code Random(123)} engine.
*
* @since 3.0
*
* @param random the PRNG used within the consumer
* @param consumer the consumer which is executed with the scope of
* the given {@code random} engine.
* @param the type of the random engine
* @throws NullPointerException if one of the arguments is {@code null}
*/
public static void using(
final R random,
final Consumer super R> consumer
) {
CONTEXT.with(
() -> random,
r -> { consumer.accept(random); return null; }
);
}
/**
* Executes the consumer code using the given {@code random} generator.
*
* {@code
* final MSeq seq = ...
* using(RandomGeneratorFactory.getDefault(), r -> {
* seq.shuffle();
* });
* }
*
* The example above shuffles the given integer {@code seq} using the
* given {@link RandomGeneratorFactory#getDefault()} factory.
*
* @since 7.0
*
* @param factory the random generator factory used within the consumer
* @param consumer the consumer which is executed within the scope of
* the given random generator.
* @param the type of the random engine
* @throws NullPointerException if one of the arguments is {@code null}
*/
public static void using(
final RandomGeneratorFactory extends R> factory,
final Consumer super R> consumer
) {
CONTEXT.with(
new TLR<>(factory::create),
r -> { consumer.accept(r.get()); return null; }
);
}
/**
* Executes the consumer code using the given {@code random} generator
* supplier.
*
* {@code
* final MSeq seq = ...
* using(() -> new MyRandomGenerator(), r -> {
* seq.shuffle();
* });
* }
*
* @since 7.0
*
* @param supplier the random generator supplier used within the consumer
* @param consumer the consumer which is executed within the scope of
* the given random generator.
* @param the type of the random engine
* @throws NullPointerException if one of the arguments is {@code null}
*/
public static void using(
final Supplier extends R> supplier,
final Consumer super R> consumer
) {
CONTEXT.with(
new TLR<>(supplier),
r -> { consumer.accept(r.get()); return null; }
);
}
/**
* Opens a new scope with the given random generator and executes
* the given function within it. The following example shows how to create a
* reproducible list of genotypes:
* {@code
* final List> genotypes =
* with(new LCG64ShiftRandom(123), r ->
* Genotype.of(DoubleChromosome.of(0, 10)).instances()
* .limit(50)
* .collect(toList())
* );
* }
*
* @since 3.0
*
* @param the type of the random engine
* @param the function return type
* @param random the PRNG used for the opened scope
* @param function the function to apply within the random scope
* @return the object returned by the given function
* @throws NullPointerException if one of the arguments is {@code null}
*/
public static T with(
final R random,
final Function super R, ? extends T> function
) {
return CONTEXT.with(
() -> random,
s -> function.apply(random)
);
}
/**
* Opens a new scope with the given random generator factory and
* executes the given function within it.
* {@code
* final List> genotypes =
* with(RandomGeneratorFactory.getDefault(), random ->
* Genotype.of(DoubleChromosome.of(0, 10)).instances()
* .limit(50)
* .collect(toList())
* );
* }
*
* @since 3.0
*
* @param the type of the random engine
* @param the function return type
* @param factory the PRNG used for the opened scope
* @param function the function to apply within the random scope
* @return the object returned by the given function
* @throws NullPointerException if one of the arguments is {@code null}.
*/
public static T with(
final RandomGeneratorFactory extends R> factory,
final Function super R, ? extends T> function
) {
return CONTEXT.with(
new TLR<>(factory::create),
r -> function.apply(r.get())
);
}
/**
* Opens a new scope with the given random generator supplier and
* executes the given function within it.
* {@code
* final List> genotypes =
* with(() -> new MyRandomGenerator(), random ->
* Genotype.of(DoubleChromosome.of(0, 10)).instances()
* .limit(50)
* .collect(toList())
* );
* }
*
* @since 3.0
*
* @param the type of the random engine
* @param the function return type
* @param supplier the PRNG used for the opened scope
* @param function the function to apply within the random scope
* @return the object returned by the given function
* @throws NullPointerException if one of the arguments is {@code null}.
*/
public static T with(
final Supplier extends R> supplier,
final Function super R, ? extends T> function
) {
return CONTEXT.with(
new TLR<>(supplier),
r -> function.apply(r.get())
);
}
}