MATH-1335. New package "o.a.c.m.rng" contains RNG core functionality (uniform distribution).
Utilities to interface with stress test suites. Userguide: benchmarks and stress test suites reports (MATH-1327). Project: http://git-wip-us.apache.org/repos/asf/commons-math/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-math/commit/6ddf4769 Tree: http://git-wip-us.apache.org/repos/asf/commons-math/tree/6ddf4769 Diff: http://git-wip-us.apache.org/repos/asf/commons-math/diff/6ddf4769 Branch: refs/heads/develop Commit: 6ddf4769919bb32cc43c5bf43e2f4e7526d3cda0 Parents: 7a8dc00 Author: Gilles <er...@apache.org> Authored: Fri Mar 11 01:51:58 2016 +0100 Committer: Gilles <er...@apache.org> Committed: Sun Mar 20 23:29:55 2016 +0100 ---------------------------------------------------------------------- .../apache/commons/math4/rng/RandomSource.java | 417 ++ .../math4/rng/UniformRandomProvider.java | 118 + .../math4/rng/internal/BaseProvider.java | 141 + .../math4/rng/internal/ProviderBuilder.java | 346 ++ .../math4/rng/internal/StateSettable.java | 49 + .../math4/rng/internal/package-info.java | 51 + .../rng/internal/source32/AbstractWell.java | 208 + .../rng/internal/source32/ISAACRandom.java | 270 ++ .../rng/internal/source32/IntProvider.java | 137 + .../math4/rng/internal/source32/JDKRandom.java | 95 + .../rng/internal/source32/MersenneTwister.java | 230 ++ .../rng/internal/source32/RandomIntSource.java | 30 + .../math4/rng/internal/source32/Well1024a.java | 78 + .../math4/rng/internal/source32/Well19937a.java | 80 + .../math4/rng/internal/source32/Well19937c.java | 85 + .../math4/rng/internal/source32/Well44497a.java | 83 + .../math4/rng/internal/source32/Well44497b.java | 90 + .../math4/rng/internal/source32/Well512a.java | 78 + .../rng/internal/source32/package-info.java | 52 + .../rng/internal/source64/LongProvider.java | 141 + .../internal/source64/MersenneTwister64.java | 201 + .../rng/internal/source64/RandomLongSource.java | 30 + .../math4/rng/internal/source64/SplitMix64.java | 78 + .../math4/rng/internal/source64/TwoCmres.java | 310 ++ .../rng/internal/source64/XorShift1024Star.java | 108 + .../rng/internal/source64/package-info.java | 52 + .../math4/rng/internal/util/Int2Long.java | 37 + .../math4/rng/internal/util/IntArray2Int.java | 41 + .../rng/internal/util/IntArray2LongArray.java | 44 + .../math4/rng/internal/util/Long2Int.java | 36 + .../math4/rng/internal/util/Long2IntArray.java | 50 + .../math4/rng/internal/util/Long2LongArray.java | 56 + .../rng/internal/util/LongArray2IntArray.java | 43 + .../math4/rng/internal/util/LongArray2Long.java | 41 + .../math4/rng/internal/util/LongMixInt.java | 50 + .../math4/rng/internal/util/LongMixLong.java | 56 + .../math4/rng/internal/util/NoOpConverter.java | 40 + .../math4/rng/internal/util/NumberFactory.java | 327 ++ .../math4/rng/internal/util/SeedConverter.java | 35 + .../internal/util/SeedConverterComposer.java | 56 + .../math4/rng/internal/util/SeedFactory.java | 262 ++ .../math4/rng/internal/util/package-info.java | 22 + .../apache/commons/math4/rng/package-info.java | 95 + src/site/apt/userguide/rng.apt | 228 + .../txt/userguide/rng/stress/dh/run_1/dh_1 | 146 + .../txt/userguide/rng/stress/dh/run_1/dh_10 | 139 + .../txt/userguide/rng/stress/dh/run_1/dh_11 | 148 + .../txt/userguide/rng/stress/dh/run_1/dh_12 | 172 + .../txt/userguide/rng/stress/dh/run_1/dh_13 | 168 + .../txt/userguide/rng/stress/dh/run_1/dh_2 | 139 + .../txt/userguide/rng/stress/dh/run_1/dh_3 | 173 + .../txt/userguide/rng/stress/dh/run_1/dh_4 | 140 + .../txt/userguide/rng/stress/dh/run_1/dh_5 | 140 + .../txt/userguide/rng/stress/dh/run_1/dh_6 | 146 + .../txt/userguide/rng/stress/dh/run_1/dh_7 | 204 + .../txt/userguide/rng/stress/dh/run_1/dh_8 | 201 + .../txt/userguide/rng/stress/dh/run_1/dh_9 | 143 + .../txt/userguide/rng/stress/dh/run_2/dh_1 | 146 + .../txt/userguide/rng/stress/dh/run_2/dh_10 | 172 + .../txt/userguide/rng/stress/dh/run_2/dh_11 | 259 ++ .../txt/userguide/rng/stress/dh/run_2/dh_12 | 168 + .../txt/userguide/rng/stress/dh/run_2/dh_13 | 261 ++ .../txt/userguide/rng/stress/dh/run_2/dh_2 | 140 + .../txt/userguide/rng/stress/dh/run_2/dh_3 | 139 + .../txt/userguide/rng/stress/dh/run_2/dh_4 | 171 + .../txt/userguide/rng/stress/dh/run_2/dh_5 | 143 + .../txt/userguide/rng/stress/dh/run_2/dh_6 | 260 ++ .../txt/userguide/rng/stress/dh/run_2/dh_7 | 143 + .../txt/userguide/rng/stress/dh/run_2/dh_8 | 800 ++++ .../txt/userguide/rng/stress/dh/run_2/dh_9 | 175 + .../txt/userguide/rng/stress/tu/run_1/tu_1 | 3882 ++++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_10 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_11 | 3795 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_12 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_13 | 3802 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_2 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_3 | 3807 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_4 | 3806 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_5 | 3804 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_6 | 3804 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_7 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_8 | 3804 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_1/tu_9 | 3802 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_1 | 3879 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_10 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_11 | 3795 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_12 | 3795 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_13 | 3795 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_2 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_3 | 3808 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_4 | 3805 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_5 | 3804 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_6 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_7 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_8 | 3803 +++++++++++++++++ .../txt/userguide/rng/stress/tu/run_2/tu_9 | 3795 +++++++++++++++++ src/site/site.xml | 1 + src/site/xdoc/userguide/index.xml | 7 + .../math4/rng/Providers32ParametricTest.java | 64 + .../math4/rng/Providers64ParametricTest.java | 64 + .../rng/ProvidersCommonParametricTest.java | 667 +++ .../apache/commons/math4/rng/ProvidersList.java | 157 + .../rng/internal/source32/ISAACRandomTest.java | 389 ++ .../rng/internal/source32/JDKRandomTest.java | 38 + .../internal/source32/MersenneTwisterTest.java | 240 ++ .../rng/internal/source32/Well1024aTest.java | 71 + .../rng/internal/source32/Well19937aTest.java | 109 + .../rng/internal/source32/Well19937cTest.java | 109 + .../rng/internal/source32/Well44497aTest.java | 109 + .../rng/internal/source32/Well44497bTest.java | 109 + .../rng/internal/source32/Well512aTest.java | 69 + .../source64/MersenneTwister64Test.java | 239 ++ .../rng/internal/source64/SplitMix64Test.java | 45 + .../rng/internal/source64/TwoCmresTest.java | 55 + .../internal/source64/XorShift1024StarTest.java | 55 + .../rng/internal/util/NumberFactoryTest.java | 164 + .../rng/internal/util/SeedFactoryTest.java | 111 + src/userguide/README | 15 +- src/userguide/c/rng/stdin2testu01.c | 127 + .../math4/userguide/rng/GeneratorsList.java | 57 + .../math4/userguide/rng/RandomStressTester.java | 280 ++ src/userguide/pom.xml | 30 + 122 files changed, 112502 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/RandomSource.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/RandomSource.java b/src/main/java/org/apache/commons/math4/rng/RandomSource.java new file mode 100644 index 0000000..087960c --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/RandomSource.java @@ -0,0 +1,417 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.commons.math4.rng; + +import org.apache.commons.math4.exception.MathUnsupportedOperationException; +import org.apache.commons.math4.rng.internal.ProviderBuilder; +import org.apache.commons.math4.rng.internal.BaseProvider; +import org.apache.commons.math4.rng.internal.util.SeedFactory; +import org.apache.commons.math4.rng.internal.source64.TwoCmres; + +/** + * This class provides the API for creating generators of random numbers. + * <p> + * Usage examples: + * <pre><code> + * UniformRandomProvider rng = RandomSource.create(RandomSource.MT); + * </code></pre> + * or + * <pre><code> + * final int[] seed = new int[] { 196, 9, 0, 226 }; + * UniformRandomProvider rng = RandomSource.create(RandomSource.MT, seed); + * </code></pre> + * or + * <pre><code> + * final int[] seed = RandomSource.createIntArray(256); + * UniformRandomProvider rng = RandomSource.create(RandomSource.MT, seed); + * </code></pre> + * where the first argument to method {@code create} is the identifier + * of the generator's concrete implementation, and the second the is the + * (optional) seed. + * <br> + * In the first form, a random seed will be {@link SeedFactory generated + * automatically}; the random seed generation step is explicit in the + * third form. + * </p> + * + * <p> + * Seeding is the procedure by which a value (or set of values) is + * used to <i>initialize</i> a generator instance. + * The requirement that a given seed will always result in the same + * internal state allows to create different instances of a generator + * that will produce the same sequence of pseudo-random numbers. + * </p> + * + * <p> + * The type of data used as a seed depends on the concrete implementation + * as some types may not provide enough information to fully initialize + * the generator's internal state. + * <br> + * The reference algorithm's seeding procedure (if provided) operates + * on a value of a (single) <i>native</i> type: + * Each concrete implementation's constructor creates an instance using + * the native type whose information contents is used to set the + * internal state. + * <br> + * When the seed value passed by the caller is of the native type, it is + * expected that the sequences produced will be the same as those + * produced by other implementations of the algorithm. + * <br> + * However, when the seed value passed by the caller is not of the native + * type, a transformation is performed by this library and the resulting + * native type value will <i>not</i> contain more information than the + * original seed value. + * If the algorithm's native type is "simpler" than the type passed by + * the caller, then some (unuse) information will even be lost. + * <br> + * The transformation from non-native to native seed type is arbitrary, + * as long as it does not reduce the amount of information required by + * the algorithm to initialize its state. + * The consequence of the transformation is that the sequences produced + * by this library may not be the same as the sequences produced by other + * implementations of the same algorithm! + * </p> + * + * <p> + * This class provides methods to generate random seeds (single values + * or arrays of values, of {@code int} or {@code long} types) that can + * be passed to the {@link RandomSource#create(RandomSource,Object,Object[]) + * generators factory method}. + * <br> + * Although the seed-generating methods defined in this class will likely + * return different values for all calls, there is no guarantee that the + * produced seed will result always in a "good" sequence of numbers, even + * if the generator is good. + * The only way to ensure that the selected seed will make the generator + * produce a "good" sequence is to submit that sequence to a series of + * stringent tests, as provided by tools such as + * <a href="http://www.phy.duke.edu/~rgb/General/dieharder.php">dieharder</a> + * or <a href="http://simul.iro.umontreal.ca/testu01/tu01.html">TestU01</a>. + * </p> + * + * <p> + * The current implementations have no provision for producing non-overlapping + * sequences. + * For parallel applications, a possible workaround is that each thread uses + * a generator of a different type (see {@link #TWO_CMRES_SELECT}). + * </p> + * + * <p> + * <b>Note:</b> + * Seeding is not equivalent to restoring the internal state of an + * <i>already initialized</i> generator. + * Indeed, generators can have a state that is more complex than the + * seed, and seeding is thus a transformation (from seed to state). + * Implementations do not provide the inverse transformation (from + * state to seed), hence it is not generally possible to know the seed + * that would initialize a new generator instance to the current state + * of another instance. + * Reseeding is also inefficient if the purpose is to continue the + * same sequence where another instance left off, as it would require + * to "replay" all the calls performed by that other instance (and it + * would require to know the number of calls to the primary source of + * randomness, which is also not usually accessible). + * <br> + * This factory thus provides a method for + * {@link #saveState(UniformRandomProvider) saving} the internal + * state of a generator. + * The state is encapsulated in an {@link State "opaque object"} to be + * used for {@link #restoreState(UniformRandomProvider,State) restoring} + * a generator (of the same type) to an identical state (e.g. to allow + * persistent storage, or to continue a sequence from where the original + * instance left off.). + * </p> + * + * @since 4.0 + */ +public enum RandomSource { + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.JDKRandom}. + * Native seed type: {@code Long}. + */ + JDK(ProviderBuilder.RandomSourceInternal.JDK), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.Well512a}. + * Native seed type: {@code int[]}. + */ + WELL_512_A(ProviderBuilder.RandomSourceInternal.WELL_512_A), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.Well1024a}. + * Native seed type: {@code int[]}. + */ + WELL_1024_A(ProviderBuilder.RandomSourceInternal.WELL_1024_A), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.Well19937a}. + * Native seed type: {@code int[]}. + */ + WELL_19937_A(ProviderBuilder.RandomSourceInternal.WELL_19937_A), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.Well19937c}. + * Native seed type: {@code int[]}. + */ + WELL_19937_C(ProviderBuilder.RandomSourceInternal.WELL_19937_C), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.Well44497a}. + * Native seed type: {@code int[]}. + */ + WELL_44497_A(ProviderBuilder.RandomSourceInternal.WELL_44497_A), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.Well44497b}. + * Native seed type: {@code int[]}. + */ + WELL_44497_B(ProviderBuilder.RandomSourceInternal.WELL_44497_B), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.MersenneTwister}. + * Native seed type: {@code int[]}. + */ + MT(ProviderBuilder.RandomSourceInternal.MT), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source32.ISAACRandom}. + * Native seed type: {@code int[]}. + */ + ISAAC(ProviderBuilder.RandomSourceInternal.ISAAC), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source64.SplitMix64}. + * Native seed type: {@code Long}. + */ + SPLIT_MIX_64(ProviderBuilder.RandomSourceInternal.SPLIT_MIX_64), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source64.XorShift1024Star}. + * Native seed type: {@code long[]}. + */ + XOR_SHIFT_1024_S(ProviderBuilder.RandomSourceInternal.XOR_SHIFT_1024_S), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source64.TwoCmres}. + * Native seed type: {@code Integer}. + */ + TWO_CMRES(ProviderBuilder.RandomSourceInternal.TWO_CMRES), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source64.TwoCmres}, + * with explicit selection of the two subcycle generators. + * Native seed type: {@code Integer}. + */ + TWO_CMRES_SELECT(ProviderBuilder.RandomSourceInternal.TWO_CMRES_SELECT), + /** + * Source of randomness is {@link org.apache.commons.math4.rng.internal.source64.MersenneTwister64}. + * Native seed type: {@code long[]}. + */ + MT_64(ProviderBuilder.RandomSourceInternal.MT_64); + + /** Internal identifier. */ + private final ProviderBuilder.RandomSourceInternal internalIdentifier; + + /** + * @param id Internal identifier. + */ + RandomSource(ProviderBuilder.RandomSourceInternal id) { + internalIdentifier = id; + } + + /** + * @return the internal identifier. + */ + ProviderBuilder.RandomSourceInternal getInternalIdentifier() { + return internalIdentifier; + } + + /** + * Checks whether the type of given {@code seed} is the native type + * of the implementation. + * + * @param seed Seed value. + * @return {@code true} if the seed can be passed to the builder + * for this RNG type. + */ + public boolean isNativeSeed(Object seed) { + return internalIdentifier.isNativeSeed(seed); + } + + /** + * Marker interface used to define the "save" and "restore" + * functionality of the generators. + */ + public interface State {} + + /** + * Creates a random number generator with a random seed. + * + * <p> + * Example of usage: + * <pre><code> + * UniformRandomProvider rng = RandomSource.create(Source.MT); + * </code></pre> + * </p> + * + * @param source {@link RandomSource RNG type}. + * @return the RNG. + */ + public static UniformRandomProvider create(RandomSource source) { + return create(source, null); + } + + /** + * Creates a random number generator with the given {@code seed}. + * + * <p> + * Example of usage: + * <pre><code> + * UniformRandomProvider rng = RandomSource.create(Source.TWO_CMRES_SELECT, 26219, 6, 9); + * </code></pre> + * </p> + * + * <p> + * Valid types for the {@code seed} are: + * <ul> + * <li>{@code Integer} (or {@code int})</li> + * <li>{@code Long} (or {@code long})</li> + * <li>{@code int[]}</li> + * <li>{@code long[]}</li> + * </ul> + * </p> + * + * <p> + * Notes: + * <ul> + * <li> + * When the seed type passed as argument is more complex (i.e. more + * bits can be independently chosen) than the generator's + * {@link #isNativeSeed(Object) native type}, the conversion of a + * set of different seeds will necessarily result in the same value + * of the native seed type. + * </li> + * <li> + * When the native seed type is an array, the same remark applies + * when the array contains more bits than the state of the generator. + * </li> + * <li> + * When the native seed type is an array and the {@code seed} is + * {@code null}, the size of the generated array will be 128. + * </li> + * </p> + * + * @param source {@link RandomSource RNG type}. + * @param seed Seed value. It can be {@code null} (in which case a + * random value will be used). + * @param data Additional arguments to the implementation's constructor. + * Please refer to the documentation of each specific implementation. + * @return the RNG. + * @throws MathUnsupportedOperationException if the type of the + * {@code seed} is invalid. + * @throws org.apache.commons.math4.exception.InsufficientDataException + * if data is missing to initialize the generator implemented by the + * given {@code source}. + */ + public static UniformRandomProvider create(RandomSource source, + Object seed, + Object ... data) { + return ProviderBuilder.create(source.getInternalIdentifier(), seed, data); + } + + /** + * Gets the number of elements of the set of "subcycle" generators from + * which two can be selected in order to create a {@link TwoCmres} RNG. + * + * @return the number of implemented subcycle generators. + */ + public static int numberOfCmresGenerators() { + return TwoCmres.numberOfSubcycleGenerators(); + } + + /** + * Saves the state of a RNG. + * + * @param provider Provider. + * @return the current state of the given {@code provider}. + * @throws MathUnsupportedOperationException if the {@code provider} is + * not an object created by this factory or the underlying source of + * randomness does not support this functionality. + * + * @see #restoreState(UniformRandomProvider,RandomSource.State) + */ + public static State saveState(UniformRandomProvider provider) { + if (!(provider instanceof BaseProvider)) { + throw new MathUnsupportedOperationException(); + } else { + return ((BaseProvider) provider).getState(); + } + } + + /** + * Restores the state of a RNG. + * + * @param provider Provider. + * @param state State which the {@code provider} will be set to. + * This parameter must have been obtained by a call to + * {@link #saveState(UniformRandomProvider) saveState(rng)} + * where {@code rng} is either the same object as {@code provider}, + * or an object of the same concrete type. + * @throws MathUnsupportedOperationException if the {@code provider} is + * not an object created by this factory or the underlying source of + * randomness does not support this functionality. + * @throws org.apache.commons.math4.exception.InsufficientDataException + * if it was detected that the {@code state} is incompatible with the + * given {@code provider}. + * + * @see #saveState(UniformRandomProvider) + */ + public static void restoreState(UniformRandomProvider provider, + State state) { + if (!(provider instanceof BaseProvider)) { + throw new MathUnsupportedOperationException(); + } else { + ((BaseProvider) provider).setState(state); + } + } + + /** + * Creates a number for use as a seed. + * + * @return a random number. + */ + public static int createInt() { + return SeedFactory.createInt(); + } + + /** + * Creates a number for use as a seed. + * + * @return a random number. + */ + public static long createLong() { + return SeedFactory.createLong(); + } + + /** + * Creates an array of numbers for use as a seed. + * + * @param n Size of the array to create. + * @return an array of {@code n} random numbers. + */ + public static int[] createIntArray(int n) { + return SeedFactory.createIntArray(n); + } + + /** + * Creates an array of numbers for use as a seed. + * + * @param n Size of the array to create. + * @return an array of {@code n} random numbers. + */ + public static long[] createLongArray(int n) { + return SeedFactory.createLongArray(n); + } +} http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/UniformRandomProvider.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/UniformRandomProvider.java b/src/main/java/org/apache/commons/math4/rng/UniformRandomProvider.java new file mode 100644 index 0000000..ef17f06 --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/UniformRandomProvider.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.commons.math4.rng; + +/** + * Applies to generators of random number sequences that follow a uniform + * distribution. + * + * @since 4.0 + */ +public interface UniformRandomProvider { + /** + * Generates {@code byte} values and places them into a user-supplied array. + * <p> + * The number of random bytes produced is equal to the length of the + * the byte array. + * </p> + * + * @param bytes Byte array in which to put the random bytes. + * Cannot be {@code null}. + */ + void nextBytes(byte[] bytes); + + /** + * Generates {@code byte} values and places them into a user-supplied array. + * + * <p> + * The array is filled with bytes extracted from random integers. + * This implies that the number of random bytes generated may be larger than + * the length of the byte array. + * </p> + * + * @param bytes Array in which to put the generated bytes. + * Cannot be {@code null}. + * @param start Index at which to start inserting the generated bytes. + * @param len Number of bytes to insert. + * @throws org.apache.commons.math4.exception.OutOfRangeException + * if {@code start < 0} or {@code start >= bytes.length}. + * @throws org.apache.commons.math4.exception.OutOfRangeException + * if {@code len < 0} or {@code len > bytes.length - start}. + */ + void nextBytes(byte[] bytes, + int start, + int len); + + /** + * Generates an {@code int} value. + * + * @return the next random value. + */ + int nextInt(); + + /** + * Generates an {@code int} value between 0 (inclusive) and the + * specified value (exclusive). + * + * @param n Bound on the random number to be returned. Must be positive. + * @return a random {@code int} value between 0 (inclusive) and n + * (exclusive). + * @throws org.apache.commons.math4.exception.NotStrictlyPositiveException + * if {@code n} is not positive. + */ + int nextInt(int n); + + /** + * Generates a {@code long} value. + * + * @return the next random value. + */ + long nextLong(); + + /** + * Generates a {@code long} value between 0 (inclusive) and the specified + * value (exclusive). + * + * @param n Bound on the random number to be returned. Must be positive. + * @return a random {@code long} value between 0 (inclusive) and n + * (exclusive). + * @throws org.apache.commons.math4.exception.NotStrictlyPositiveException + * if {@code n} is not positive. + */ + long nextLong(long n); + + /** + * Generates a {@code boolean} value. + * + * @return the next random value. + */ + boolean nextBoolean(); + + /** + * Generates a {@code float} value between 0 and 1. + * + * @return the next random value between 0 and 1. + */ + float nextFloat(); + + /** + * Generates a {@code double} value between 0 and 1. + * + * @return the next random value between 0 and 1. + */ + double nextDouble(); +} http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/internal/BaseProvider.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/internal/BaseProvider.java b/src/main/java/org/apache/commons/math4/rng/internal/BaseProvider.java new file mode 100644 index 0000000..e29d854 --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/internal/BaseProvider.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package org.apache.commons.math4.rng.internal; + +import java.util.Arrays; +import java.io.Serializable; +import org.apache.commons.math4.exception.MathUnsupportedOperationException; +import org.apache.commons.math4.exception.NotStrictlyPositiveException; +import org.apache.commons.math4.rng.UniformRandomProvider; +import org.apache.commons.math4.rng.RandomSource; + +/** + * Base class with default implementation for common methods. + */ +public abstract class BaseProvider + implements UniformRandomProvider, + StateSettable { + /** {@inheritDoc} */ + @Override + public int nextInt(int n) throws IllegalArgumentException { + if (n > 0) { + if ((n & -n) == n) { + return (int) ((n * (long) (nextInt() >>> 1)) >> 31); + } + int bits; + int val; + do { + bits = nextInt() >>> 1; + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; + } + + throw new NotStrictlyPositiveException(n); + } + + /** {@inheritDoc} */ + @Override + public long nextLong(long n) { + if (n > 0) { + long bits; + long val; + do { + bits = nextLong() >>> 1; + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; + } + + throw new NotStrictlyPositiveException(n); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return getClass().getName(); + } + + /** {@inheritDoc} */ + @Override + public RandomSource.State getState() { + return new State(getStateInternal()); + } + + /** {@inheritDoc} */ + @Override + public void setState(RandomSource.State state) { + // Cast will intentionally fail if the argument is not one we created. + final State s = (State) state; + setStateInternal(s.getState()); + } + + /** + * Creates a snapshot of the RNG state. + * + * @return the internal state. + * @throws MathUnsupportedOperationException if not implemented. + */ + protected byte[] getStateInternal() { + throw new MathUnsupportedOperationException(); + } + + /** + * Resets the RNG to the given {@code state}. + * + * @param state State (previously obtained by a call to + * {@link #getStateInternal()}). + * @throws MathUnsupportedOperationException if not implemented. + */ + protected void setStateInternal(byte[] state) { + throw new MathUnsupportedOperationException(); + } + + /** + * "Black-box" state. + * Its sole purpose is to store all the data needed to recover + * the same state in order to restart a sequence where it left + * off. + * External code should not to modify the data contained in + * instances of this class. + */ + private static class State + implements RandomSource.State, + Serializable { + /** Serializable version identifier. */ + private static final long serialVersionUID = 4720160226L; + /** Internal state. */ + private byte[] state; + + /** + * @param state Mapping of all the data which a subclass of + * {@link BaseProvider} needs in order to reset its internal + * state. + */ + State(byte[] state) { + this.state = Arrays.copyOf(state, state.length); + } + + /** + * @return the internal state. + */ + byte[] getState() { + return Arrays.copyOf(state, state.length); + } + } +} http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/internal/ProviderBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/internal/ProviderBuilder.java b/src/main/java/org/apache/commons/math4/rng/internal/ProviderBuilder.java new file mode 100644 index 0000000..f38b679 --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/internal/ProviderBuilder.java @@ -0,0 +1,346 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.commons.math4.rng.internal; + +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.math4.exception.MathUnsupportedOperationException; +import org.apache.commons.math4.exception.MathInternalError; +import org.apache.commons.math4.rng.UniformRandomProvider; +import org.apache.commons.math4.rng.internal.util.SeedFactory; +import org.apache.commons.math4.rng.internal.util.NoOpConverter; +import org.apache.commons.math4.rng.internal.util.Int2Long; +import org.apache.commons.math4.rng.internal.util.Long2Int; +import org.apache.commons.math4.rng.internal.util.Long2IntArray; +import org.apache.commons.math4.rng.internal.util.Long2LongArray; +import org.apache.commons.math4.rng.internal.util.IntArray2LongArray; +import org.apache.commons.math4.rng.internal.util.LongArray2IntArray; +import org.apache.commons.math4.rng.internal.util.LongArray2Long; +import org.apache.commons.math4.rng.internal.util.IntArray2Int; +import org.apache.commons.math4.rng.internal.util.SeedConverter; +import org.apache.commons.math4.rng.internal.util.SeedConverterComposer; +import org.apache.commons.math4.rng.internal.source32.JDKRandom; +import org.apache.commons.math4.rng.internal.source32.Well512a; +import org.apache.commons.math4.rng.internal.source32.Well1024a; +import org.apache.commons.math4.rng.internal.source32.Well19937a; +import org.apache.commons.math4.rng.internal.source32.Well19937c; +import org.apache.commons.math4.rng.internal.source32.Well44497a; +import org.apache.commons.math4.rng.internal.source32.Well44497b; +import org.apache.commons.math4.rng.internal.source32.ISAACRandom; +import org.apache.commons.math4.rng.internal.source32.MersenneTwister; +import org.apache.commons.math4.rng.internal.source64.SplitMix64; +import org.apache.commons.math4.rng.internal.source64.XorShift1024Star; +import org.apache.commons.math4.rng.internal.source64.TwoCmres; +import org.apache.commons.math4.rng.internal.source64.MersenneTwister64; + +/** + * RNG builder. + * <p> + * It uses reflection to find the factory method of the RNG implementation, + * and performs seed type conversions. + * </p> + */ +public class ProviderBuilder { + /** Length of the seed array (for random seed). */ + private static final int RANDOM_SEED_ARRAY_SIZE = 128; + /** Seed converter. */ + private static final Long2Int LONG_TO_INT = new Long2Int(); + /** Seed converter. */ + private static final Int2Long INT_TO_LONG = new Int2Long(); + /** Seed converter. */ + private static final Long2IntArray LONG_TO_INT_ARRAY = new Long2IntArray(RANDOM_SEED_ARRAY_SIZE); + /** Seed converter. */ + private static final Long2LongArray LONG_TO_LONG_ARRAY = new Long2LongArray(RANDOM_SEED_ARRAY_SIZE); + /** Seed converter. */ + private static final LongArray2Long LONG_ARRAY_TO_LONG = new LongArray2Long(); + /** Seed converter. */ + private static final IntArray2Int INT_ARRAY_TO_INT = new IntArray2Int(); + /** Seed converter. */ + private static final LongArray2IntArray LONG_ARRAY_TO_INT_ARRAY = new LongArray2IntArray(); + /** Seed converter. */ + private static final IntArray2LongArray INT_ARRAY_TO_LONG_ARRAY = new IntArray2LongArray(); + /** Map to convert "Integer" seeds. */ + private static final Map<Class<?>, SeedConverter<Integer,?>> CONV_INT = new HashMap<>(); + /** Map to convert "int[]" seeds. */ + private static final Map<Class<?>, SeedConverter<int[],?>> CONV_INT_ARRAY = new HashMap<>(); + /** Map to convert "Long" seeds. */ + private static final Map<Class<?>, SeedConverter<Long,?>> CONV_LONG = new HashMap<>(); + /** Map to convert "long[]" seeds. */ + private static final Map<Class<?>, SeedConverter<long[],?>> CONV_LONG_ARRAY = new HashMap<>(); + + static { + // Input seed type is "Long". + // Key is the implementation's "native" seed type. + CONV_LONG.put(Integer.class, LONG_TO_INT); + CONV_LONG.put(Long.class, new NoOpConverter<Long>()); + CONV_LONG.put(int[].class, LONG_TO_INT_ARRAY); + CONV_LONG.put(long[].class, LONG_TO_LONG_ARRAY); + + // Input seed type is "Integer". + // Key is the implementation's "native" seed type. + CONV_INT.put(Integer.class, new NoOpConverter<Integer>()); + CONV_INT.put(Long.class, INT_TO_LONG); + CONV_INT.put(int[].class, new SeedConverterComposer<Integer,Long,int[]>(INT_TO_LONG, LONG_TO_INT_ARRAY)); + CONV_INT.put(long[].class, new SeedConverterComposer<Integer,Long,long[]>(INT_TO_LONG, LONG_TO_LONG_ARRAY)); + + // Input seed type is "int[]". + // Key is the implementation's "native" seed type. + CONV_INT_ARRAY.put(Integer.class, INT_ARRAY_TO_INT); + CONV_INT_ARRAY.put(Long.class, new SeedConverterComposer<int[],Integer,Long>(INT_ARRAY_TO_INT, INT_TO_LONG)); + CONV_INT_ARRAY.put(int[].class, new NoOpConverter<int[]>()); + CONV_INT_ARRAY.put(long[].class, INT_ARRAY_TO_LONG_ARRAY); + + // Input seed type is "long[]". + // Key is the implementation's "native" seed type. + CONV_LONG_ARRAY.put(Integer.class, new SeedConverterComposer<long[],Long,Integer>(LONG_ARRAY_TO_LONG, LONG_TO_INT)); + CONV_LONG_ARRAY.put(Long.class, LONG_ARRAY_TO_LONG); + CONV_LONG_ARRAY.put(int[].class, LONG_ARRAY_TO_INT_ARRAY); + CONV_LONG_ARRAY.put(long[].class, new NoOpConverter<long[]>()); + } + + /** + * Class only contains static method. + */ + private ProviderBuilder() {} + + /** + * Creates a RNG instance. + * + * @param source RNG specification. + * @param seed Seed value. It can be {@code null} (in which case a + * random value will be used). + * @param args Additional arguments to the implementation's constructor. + * @return a new RNG instance. + * @throws MathUnsupportedOperationException if the seed type is + * invalid. + */ + public static UniformRandomProvider create(RandomSourceInternal source, + Object seed, + Object[] args) { + // Convert seed to native type. + final Object nativeSeed = createSeed(source, seed); + + // Build a single array with all the arguments to be passed + // (in the right order) to the constructor. + final List<Object> all = new ArrayList<>(); + all.add(nativeSeed); + if (args != null) { + all.addAll(Arrays.asList(args)); + } + + // Instantiate. + return create(createConstructor(source), all.toArray()); + } + + /** + * Creates a native seed from any of the supported seed types. + * + * @param source Source. + * @param seed Input seed. + * @return the native seed. + * @throw MathUnsupportedOperationException if the {@code seed} type + * is invalid. + */ + private static Object createSeed(RandomSourceInternal source, + Object seed) { + Object nativeSeed = null; + + if (seed == null) { + // Create a random seed of the appropriate native type. + + if (source.getSeed().equals(Integer.class)) { + nativeSeed = SeedFactory.createInt(); + } else if (source.getSeed().equals(Long.class)) { + nativeSeed = SeedFactory.createLong(); + } else if (source.getSeed().equals(int[].class)) { + nativeSeed = SeedFactory.createIntArray(RANDOM_SEED_ARRAY_SIZE); + } else if (source.getSeed().equals(long[].class)) { + nativeSeed = SeedFactory.createLongArray(RANDOM_SEED_ARRAY_SIZE); + } + } else { + // Convert to native type. + + if (seed instanceof Integer) { + nativeSeed = CONV_INT.get(source.getSeed()).convert((Integer) seed); + } else if (seed instanceof Long) { + nativeSeed = CONV_LONG.get(source.getSeed()).convert((Long) seed); + } else if (seed instanceof int[]) { + nativeSeed = CONV_INT_ARRAY.get(source.getSeed()).convert((int[]) seed); + } else if (seed instanceof long[]) { + nativeSeed = CONV_LONG_ARRAY.get(source.getSeed()).convert((long[]) seed); + } + + if (nativeSeed == null) { + // Since the input seed was not null, getting here means that + // no suitable converter is present in the maps. + throw new MathUnsupportedOperationException(); + } + + if (!source.isNativeSeed(nativeSeed)) { + // Conversion setup is wrong. + throw new MathInternalError(); + } + } + + return nativeSeed; + } + + /** + * Creates a constructor. + * + * @param source RNG specification. + * @return a RNG constructor. + */ + private static Constructor<?> createConstructor(RandomSourceInternal source) { + try { + return source.getRng().getConstructor(source.getArgs()); + } catch (NoSuchMethodException e) { + // Info in "RandomSourceInternal" is inconsistent with the + // constructor of the implementation. + throw new MathInternalError(e); + } + } + + /** + * Creates a RNG. + * + * @param rng RNG specification. + * @param args Arguments to the implementation's constructor. + * @return a new RNG instance. + */ + private static UniformRandomProvider create(Constructor<?> rng, + Object[] args) { + try { + return (UniformRandomProvider) rng.newInstance(args); + } catch (InvocationTargetException | + InstantiationException | + IllegalArgumentException | + IllegalAccessException e) { + throw new MathInternalError(e); + } + } + + /** + * Identifiers of the generators. + */ + public enum RandomSourceInternal { + /** Source of randomness is {@link JDKRandom}. */ + JDK(JDKRandom.class, + Long.class), + /** Source of randomness is {@link Well512a}. */ + WELL_512_A(Well512a.class, + int[].class), + /** Source of randomness is {@link Well1024a}. */ + WELL_1024_A(Well1024a.class, + int[].class), + /** Source of randomness is {@link Well19937a}. */ + WELL_19937_A(Well19937a.class, + int[].class), + /** Source of randomness is {@link Well19937c}. */ + WELL_19937_C(Well19937c.class, + int[].class), + /** Source of randomness is {@link Well44497a}. */ + WELL_44497_A(Well44497a.class, + int[].class), + /** Source of randomness is {@link Well44497b}. */ + WELL_44497_B(Well44497b.class, + int[].class), + /** Source of randomness is {@link MersenneTwister}. */ + MT(MersenneTwister.class, + int[].class), + /** Source of randomness is {@link ISAACRandom}. */ + ISAAC(ISAACRandom.class, + int[].class), + /** Source of randomness is {@link SplitMix64}. */ + SPLIT_MIX_64(SplitMix64.class, + Long.class), + /** Source of randomness is {@link XorShift1024Star}. */ + XOR_SHIFT_1024_S(XorShift1024Star.class, + long[].class), + /** Source of randomness is {@link TwoCmres}. */ + TWO_CMRES(TwoCmres.class, + Integer.class), + /** + * Source of randomness is {@link TwoCmres} with explicit selection + * of the two subcycle generators. + */ + TWO_CMRES_SELECT(TwoCmres.class, + Integer.class, + Integer.TYPE, + Integer.TYPE), + /** Source of randomness is {@link MersenneTwister64}. */ + MT_64(MersenneTwister64.class, + long[].class); + + /** Source type. */ + private final Class<? extends UniformRandomProvider> rng; + /** Data needed to build the generator. */ + private final Class<?>[] args; + + /** + * @param rng Source type. + * @param args Data needed to create a generator instance. + * The first element must be the native seed type. + */ + RandomSourceInternal(Class<? extends UniformRandomProvider> rng, + Class<?> ... args) { + this.rng = rng; + this.args = Arrays.copyOf(args, args.length); + } + + /** + * @return the source type. + */ + public Class<?> getRng() { + return rng; + } + + /** + * @return the seed type. + */ + Class<?> getSeed() { + return args[0]; + } + + /** + * @return the data needed to build the generator. + */ + Class<?>[] getArgs() { + return args; + } + + /** + * Checks whether the type of given {@code seed} is the native type + * of the implementation. + * + * @param <SEED> Seed type. + * + * @param seed Seed value. + * @return {@code true} if the seed can be passed to the builder + * for this RNG type. + */ + public <SEED> boolean isNativeSeed(SEED seed) { + return getSeed().equals(seed.getClass()); + } + } +} http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/internal/StateSettable.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/internal/StateSettable.java b/src/main/java/org/apache/commons/math4/rng/internal/StateSettable.java new file mode 100644 index 0000000..6c7e7a5 --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/internal/StateSettable.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.commons.math4.rng.internal; + +import org.apache.commons.math4.rng.RandomSource; + +/** + * Indicates that the state of the instance can be saved and restored. + * + * @since 4.0 + */ +public interface StateSettable { + /** + * Sets the instance's state. + * + * @param state State. The given argument must have been retrieved + * by a call to {@link #getState()}. + * + * @throws org.apache.commons.math4.exception.MathUnsupportedOperationException + * if not implemented. + */ + void setState(RandomSource.State state); + + /** + * Gets the instance's state. + * + * @return the current state. The given argument can then be passed + * to {@link #setState(RandomSource.State)} in order to recover the + * current state. + * + * @throws org.apache.commons.math4.exception.MathUnsupportedOperationException + * if not implemented. + */ + RandomSource.State getState(); +} http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/internal/package-info.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/internal/package-info.java b/src/main/java/org/apache/commons/math4/rng/internal/package-info.java new file mode 100644 index 0000000..47ad828 --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/internal/package-info.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/** + * <h3>Base classes for the {@link org.apache.commons.math4.rng.UniformRandomProvider + * generation of uniformly distributed random numbers}. + * </h3> + * + * <p> + * <b>For internal use only:</b> Direct access to classes in this package + * and below, is discouraged, as they could be modified without notice. + * </p> + * + * <p><b>Notes for developers</b></p> + * + * <p> + * This package contains the common functionality. + * <br> + * Implementations that produce + * {@link org.apache.commons.math4.rng.internal.source32.RandomIntSource int} + * values are defined in the + * {@link org.apache.commons.math4.rng.internal.source32 source32} package. + * <br> + * Implementations that produce + * {@link org.apache.commons.math4.rng.internal.source64.RandomLongSource long} + * values are defined in the + * {@link org.apache.commons.math4.rng.internal.source64 source64} package. + * </p> + * + * <p> + * Each implementation must have an identifier in + * {@link org.apache.commons.math4.rng.internal.ProviderBuilder.RandomSourceInternal} + * which must be referred to from the {@link org.apache.commons.math4.rng.RandomSource public API}. + * </p> + */ + +package org.apache.commons.math4.rng.internal; http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/internal/source32/AbstractWell.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/internal/source32/AbstractWell.java b/src/main/java/org/apache/commons/math4/rng/internal/source32/AbstractWell.java new file mode 100644 index 0000000..c9737db --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/internal/source32/AbstractWell.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.commons.math4.rng.internal.source32; + +import java.util.Arrays; +import org.apache.commons.math4.exception.InsufficientDataException; +import org.apache.commons.math4.rng.internal.util.NumberFactory; + +/** + * This abstract class implements the WELL class of pseudo-random number + * generator from François Panneton, Pierre L'Ecuyer and Makoto + * Matsumoto. + * <p> + * This generator is described in a paper by François Panneton, + * Pierre L'Ecuyer and Makoto Matsumoto + * <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf"> + * Improved Long-Period Generators Based on Linear Recurrences Modulo 2</a> + * ACM Transactions on Mathematical Software, 32, 1 (2006). + * The errata for the paper are in + * <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>. + * </p> + * + * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number generator</a> + * + * @since 4.0 + */ +public abstract class AbstractWell extends IntProvider { + /** Current index in the bytes pool. */ + protected int index; + /** Bytes pool. */ + protected final int[] v; + + /** + * Creates a new random number generator using an int array seed. + * + * @param k Number of bits in the pool (not necessarily a multiple of 32). + * @param seed Initial seed. + */ + protected AbstractWell(final int k, + final int[] seed) { + final int r = calculateBlockCount(k); + v = new int[r]; + index = 0; + + // Initialize the pool content. + setSeedInternal(seed); + } + + /** {@inheritDoc} */ + @Override + protected byte[] getStateInternal() { + final int[] s = Arrays.copyOf(v, v.length + 1); + s[v.length] = index; + + return NumberFactory.makeByteArray(s); + } + + /** {@inheritDoc} */ + @Override + protected void setStateInternal(byte[] s) { + if (s.length != (v.length + 1) * 4) { + throw new InsufficientDataException(); + } + + final int[] tmp = NumberFactory.makeIntArray(s); + + System.arraycopy(tmp, 0, v, 0, v.length); + index = tmp[v.length]; + } + + /** + * Reinitialize the generator as if just built with the given int array seed. + * + * <p>The state of the generator is exactly the same as a new generator built + * with the same seed.</p> + * + * @param seed Seed. Cannot be null. + */ + private void setSeedInternal(final int[] seed) { + System.arraycopy(seed, 0, v, 0, Math.min(seed.length, v.length)); + + if (seed.length < v.length) { + for (int i = seed.length; i < v.length; ++i) { + final long current = v[i - seed.length]; + v[i] = (int) ((1812433253L * (current ^ (current >> 30)) + i) & 0xffffffffL); + } + } + + index = 0; + } + + /** + * Calculate the number of 32-bits blocks. + * + * @param k Number of bits in the pool (not necessarily a multiple of 32). + * @return the number of 32-bits blocks. + */ + private static int calculateBlockCount(final int k) { + // the bits pool contains k bits, k = r w - p where r is the number + // of w bits blocks, w is the block size (always 32 in the original paper) + // and p is the number of unused bits in the last block + final int w = 32; + final int r = (k + w - 1) / w; + return r; + } + + /** + * Inner class used to store the indirection index table which is fixed for a given + * type of WELL class of pseudo-random number generator. + */ + protected static final class IndexTable { + /** Index indirection table giving for each index its predecessor taking table size into account. */ + private final int[] iRm1; + /** Index indirection table giving for each index its second predecessor taking table size into account. */ + private final int[] iRm2; + /** Index indirection table giving for each index the value index + m1 taking table size into account. */ + private final int[] i1; + /** Index indirection table giving for each index the value index + m2 taking table size into account. */ + private final int[] i2; + /** Index indirection table giving for each index the value index + m3 taking table size into account. */ + private final int[] i3; + + /** Creates a new pre-calculated indirection index table. + * @param k number of bits in the pool (not necessarily a multiple of 32) + * @param m1 first parameter of the algorithm + * @param m2 second parameter of the algorithm + * @param m3 third parameter of the algorithm + */ + public IndexTable(final int k, final int m1, final int m2, final int m3) { + + final int r = calculateBlockCount(k); + + // precompute indirection index tables. These tables are used for optimizing access + // they allow saving computations like "(j + r - 2) % r" with costly modulo operations + iRm1 = new int[r]; + iRm2 = new int[r]; + i1 = new int[r]; + i2 = new int[r]; + i3 = new int[r]; + for (int j = 0; j < r; ++j) { + iRm1[j] = (j + r - 1) % r; + iRm2[j] = (j + r - 2) % r; + i1[j] = (j + m1) % r; + i2[j] = (j + m2) % r; + i3[j] = (j + m3) % r; + } + } + + /** + * Returns the predecessor of the given index modulo the table size. + * @param index the index to look at + * @return (index - 1) % table size + */ + public int getIndexPred(final int index) { + return iRm1[index]; + } + + /** + * Returns the second predecessor of the given index modulo the table size. + * @param index the index to look at + * @return (index - 2) % table size + */ + public int getIndexPred2(final int index) { + return iRm2[index]; + } + + /** + * Returns index + M1 modulo the table size. + * @param index the index to look at + * @return (index + M1) % table size + */ + public int getIndexM1(final int index) { + return i1[index]; + } + + /** + * Returns index + M2 modulo the table size. + * @param index the index to look at + * @return (index + M2) % table size + */ + public int getIndexM2(final int index) { + return i2[index]; + } + + /** + * Returns index + M3 modulo the table size. + * @param index the index to look at + * @return (index + M3) % table size + */ + public int getIndexM3(final int index) { + return i3[index]; + } + } +} http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/internal/source32/ISAACRandom.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/internal/source32/ISAACRandom.java b/src/main/java/org/apache/commons/math4/rng/internal/source32/ISAACRandom.java new file mode 100644 index 0000000..c7dd73a --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/internal/source32/ISAACRandom.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package org.apache.commons.math4.rng.internal.source32; + +import java.util.Arrays; +import org.apache.commons.math4.exception.InsufficientDataException; +import org.apache.commons.math4.rng.internal.util.NumberFactory; + +/** + * A fast cryptographic pseudo-random number generator. + * <p> + * ISAAC (Indirection, Shift, Accumulate, Add, and Count) generates 32-bit + * random numbers. + * ISAAC has been designed to be cryptographically secure and is inspired + * by RC4. + * Cycles are guaranteed to be at least 2<sup>40</sup> values long, and they + * are 2<sup>8295</sup> values long on average. + * The results are uniformly distributed, unbiased, and unpredictable unless + * you know the seed. + * <p> + * This code is based (with minor changes and improvements) on the original + * implementation of the algorithm by Bob Jenkins. + * + * @see <a href="http://burtleburtle.net/bob/rand/isaacafa.html"> + * ISAAC: a fast cryptographic pseudo-random number generator</a> + * + * @since 4.0 + */ +public class ISAACRandom extends IntProvider { + /** Log of size of rsl[] and mem[] */ + private static final int SIZE_L = 8; + /** Size of rsl[] and mem[] */ + private static final int SIZE = 1 << SIZE_L; + /** Half-size of rsl[] and mem[] */ + private static final int H_SIZE = SIZE >> 1; + /** For pseudo-random lookup */ + private static final int MASK = SIZE - 1 << 2; + /** The golden ratio */ + private static final int GLD_RATIO = 0x9e3779b9; + /** The results given to the user */ + private final int[] rsl = new int[SIZE]; + /** The internal state */ + private final int[] mem = new int[SIZE]; + /** Count through the results in rsl[] */ + private int count; + /** Accumulator */ + private int isaacA; + /** The last result */ + private int isaacB; + /** Counter, guarantees cycle is at least 2^40 */ + private int isaacC; + /** Service variable. */ + private final int[] arr = new int[8]; + /** Service variable. */ + private int isaacX; + /** Service variable. */ + private int isaacI; + /** Service variable. */ + private int isaacJ; + + /** + * Creates a new ISAAC random number generator. + * + * @param seed Initial seed + */ + public ISAACRandom(int[] seed) { + setSeedInternal(seed); + } + + /** {@inheritDoc} */ + @Override + protected byte[] getStateInternal() { + final int[] sRsl = Arrays.copyOf(rsl, SIZE); + final int[] sMem = Arrays.copyOf(mem, SIZE); + final int[] sRem = Arrays.copyOf(new int[] { count, isaacA, isaacB, isaacC }, 4); + + final int[] s = new int[2 * SIZE + sRem.length]; + System.arraycopy(sRsl, 0, s, 0, SIZE); + System.arraycopy(sMem, 0, s, SIZE, SIZE); + System.arraycopy(sRem, 0, s, 2 * SIZE, sRem.length); + + return NumberFactory.makeByteArray(s); + } + + /** {@inheritDoc} */ + @Override + protected void setStateInternal(byte[] s) { + if (s.length != (2 * SIZE + 4) * 4) { + throw new InsufficientDataException(); + } + + final int[] tmp = NumberFactory.makeIntArray(s); + + System.arraycopy(tmp, 0, rsl, 0, SIZE); + System.arraycopy(tmp, SIZE, mem, 0, SIZE); + final int offset = 2 * SIZE; + count = tmp[offset]; + isaacA = tmp[offset + 1]; + isaacB = tmp[offset + 2]; + isaacC = tmp[offset + 3]; + } + + /** + * Reseeds the RNG. + * + * @param seed Seed. Cannot be null. + */ + private void setSeedInternal(int[] seed) { + final int seedLen = seed.length; + final int rslLen = rsl.length; + System.arraycopy(seed, 0, rsl, 0, Math.min(seedLen, rslLen)); + if (seedLen < rslLen) { + for (int j = seedLen; j < rslLen; j++) { + long k = rsl[j - seedLen]; + rsl[j] = (int) (0x6c078965L * (k ^ k >> 30) + j & 0xffffffffL); + } + } + initState(); + } + + /** {@inheritDoc} */ + @Override + public int next() { + if (count < 0) { + isaac(); + count = SIZE - 1; + } + return rsl[count--]; + } + + /** Generate 256 results */ + private void isaac() { + isaacI = 0; + isaacJ = H_SIZE; + isaacB += ++isaacC; + while (isaacI < H_SIZE) { + isaac2(); + } + isaacJ = 0; + while (isaacJ < H_SIZE) { + isaac2(); + } + } + + /** Intermediate internal loop. */ + private void isaac2() { + isaacX = mem[isaacI]; + isaacA ^= isaacA << 13; + isaacA += mem[isaacJ++]; + isaac3(); + isaacX = mem[isaacI]; + isaacA ^= isaacA >>> 6; + isaacA += mem[isaacJ++]; + isaac3(); + isaacX = mem[isaacI]; + isaacA ^= isaacA << 2; + isaacA += mem[isaacJ++]; + isaac3(); + isaacX = mem[isaacI]; + isaacA ^= isaacA >>> 16; + isaacA += mem[isaacJ++]; + isaac3(); + } + + /** Lowest level internal loop. */ + private void isaac3() { + mem[isaacI] = mem[(isaacX & MASK) >> 2] + isaacA + isaacB; + isaacB = mem[(mem[isaacI] >> SIZE_L & MASK) >> 2] + isaacX; + rsl[isaacI++] = isaacB; + } + + /** Initialize, or reinitialize, this instance of rand. */ + private void initState() { + isaacA = 0; + isaacB = 0; + isaacC = 0; + for (int j = 0; j < arr.length; j++) { + arr[j] = GLD_RATIO; + } + for (int j = 0; j < 4; j++) { + shuffle(); + } + // fill in mem[] with messy stuff + for (int j = 0; j < SIZE; j += 8) { + arr[0] += rsl[j]; + arr[1] += rsl[j + 1]; + arr[2] += rsl[j + 2]; + arr[3] += rsl[j + 3]; + arr[4] += rsl[j + 4]; + arr[5] += rsl[j + 5]; + arr[6] += rsl[j + 6]; + arr[7] += rsl[j + 7]; + shuffle(); + setState(j); + } + // second pass makes all of seed affect all of mem + for (int j = 0; j < SIZE; j += 8) { + arr[0] += mem[j]; + arr[1] += mem[j + 1]; + arr[2] += mem[j + 2]; + arr[3] += mem[j + 3]; + arr[4] += mem[j + 4]; + arr[5] += mem[j + 5]; + arr[6] += mem[j + 6]; + arr[7] += mem[j + 7]; + shuffle(); + setState(j); + } + isaac(); + count = SIZE - 1; + } + + /** Shuffle array. */ + private void shuffle() { + arr[0] ^= arr[1] << 11; + arr[3] += arr[0]; + arr[1] += arr[2]; + arr[1] ^= arr[2] >>> 2; + arr[4] += arr[1]; + arr[2] += arr[3]; + arr[2] ^= arr[3] << 8; + arr[5] += arr[2]; + arr[3] += arr[4]; + arr[3] ^= arr[4] >>> 16; + arr[6] += arr[3]; + arr[4] += arr[5]; + arr[4] ^= arr[5] << 10; + arr[7] += arr[4]; + arr[5] += arr[6]; + arr[5] ^= arr[6] >>> 4; + arr[0] += arr[5]; + arr[6] += arr[7]; + arr[6] ^= arr[7] << 8; + arr[1] += arr[6]; + arr[7] += arr[0]; + arr[7] ^= arr[0] >>> 9; + arr[2] += arr[7]; + arr[0] += arr[1]; + } + + /** Set the state by copying the internal arrays. + * + * @param start First index into {@link #mem} array. + */ + private void setState(int start) { + mem[start] = arr[0]; + mem[start + 1] = arr[1]; + mem[start + 2] = arr[2]; + mem[start + 3] = arr[3]; + mem[start + 4] = arr[4]; + mem[start + 5] = arr[5]; + mem[start + 6] = arr[6]; + mem[start + 7] = arr[7]; + } +} http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/internal/source32/IntProvider.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/internal/source32/IntProvider.java b/src/main/java/org/apache/commons/math4/rng/internal/source32/IntProvider.java new file mode 100644 index 0000000..bae33fc --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/internal/source32/IntProvider.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package org.apache.commons.math4.rng.internal.source32; + +import org.apache.commons.math4.exception.OutOfRangeException; +import org.apache.commons.math4.rng.internal.util.NumberFactory; +import org.apache.commons.math4.rng.internal.BaseProvider; + +/** + * Base class for all implementations that provide an {@code int}-based + * source randomness. + */ +public abstract class IntProvider + extends BaseProvider + implements RandomIntSource { + + /** {@inheritDoc} */ + @Override + public abstract int next(); + + /** {@inheritDoc} */ + @Override + public int nextInt() { + return next(); + } + + /** {@inheritDoc} */ + @Override + public boolean nextBoolean() { + return NumberFactory.makeBoolean(nextInt()); + } + + /** {@inheritDoc} */ + @Override + public double nextDouble() { + return NumberFactory.makeDouble(nextInt(), nextInt()); + } + + /** {@inheritDoc} */ + @Override + public float nextFloat() { + return NumberFactory.makeFloat(nextInt()); + } + + /** {@inheritDoc} */ + @Override + public long nextLong() { + return NumberFactory.makeLong(nextInt(), nextInt()); + } + + /** {@inheritDoc} */ + @Override + public void nextBytes(byte[] bytes) { + nextBytesFill(this, bytes, 0, bytes.length); + } + + /** {@inheritDoc} */ + @Override + public void nextBytes(byte[] bytes, + int start, + int len) { + if (start < 0 || + start >= bytes.length) { + throw new OutOfRangeException(start, 0, bytes.length); + } + if (len < 0 || + len > bytes.length - start) { + throw new OutOfRangeException(len, 0, bytes.length - start); + } + + nextBytesFill(this, bytes, start, len); + } + + /** + * Generates random bytes and places them into a user-supplied array. + * + * <p> + * The array is filled with bytes extracted from random {@code int} values. + * This implies that the number of random bytes generated may be larger than + * the length of the byte array. + * </p> + * + * @param source Source of randomness. + * @param bytes Array in which to put the generated bytes. Cannot be null. + * @param start Index at which to start inserting the generated bytes. + * @param len Number of bytes to insert. + */ + static void nextBytesFill(RandomIntSource source, + byte[] bytes, + int start, + int len) { + int index = start; // Index of first insertion. + + // Index of first insertion plus multiple of 4 part of length + // (i.e. length with 2 least significant bits unset). + final int indexLoopLimit = index + (len & 0x7ffffffc); + + // Start filling in the byte array, 4 bytes at a time. + while (index < indexLoopLimit) { + final int random = source.next(); + bytes[index++] = (byte) random; + bytes[index++] = (byte) (random >>> 8); + bytes[index++] = (byte) (random >>> 16); + bytes[index++] = (byte) (random >>> 24); + } + + final int indexLimit = start + len; // Index of last insertion + 1. + + // Fill in the remaining bytes. + if (index < indexLimit) { + int random = source.next(); + while (true) { + bytes[index++] = (byte) random; + if (index < indexLimit) { + random >>>= 8; + } else { + break; + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/commons-math/blob/6ddf4769/src/main/java/org/apache/commons/math4/rng/internal/source32/JDKRandom.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/math4/rng/internal/source32/JDKRandom.java b/src/main/java/org/apache/commons/math4/rng/internal/source32/JDKRandom.java new file mode 100644 index 0000000..e393c12 --- /dev/null +++ b/src/main/java/org/apache/commons/math4/rng/internal/source32/JDKRandom.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.commons.math4.rng.internal.source32; + +import java.util.Random; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; + +/** + * A provider that uses the {@link Random#nextInt()} method of the JDK's + * {@code Random} class as the source of randomness. + * + * <p> + * <b>Caveat:</b> All the other calls will be redirected to the methods + * implemented within this library. + * </p> + * + * <p> + * The state of this source of randomness is saved and restored through + * the serialization of the {@link Random} instance. + * </p> + * + * @since 4.0 + */ +public class JDKRandom extends IntProvider { + /** Delegate. Cannot be "final" (to allow serialization). */ + private Random delegate; + + /** + * Creates an instance with the given seed. + * + * @param seed Initial seed. + */ + public JDKRandom(Long seed) { + delegate = new Random(seed); + } + + /** + * {@inheritDoc} + * + * @see Random#nextInt() + */ + @Override + public int next() { + return delegate.nextInt(); + } + + /** {@inheritDoc} */ + @Override + protected byte[] getStateInternal() { + try { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(bos); + + // Serialize the "delegate". + oos.writeObject(delegate); + + return bos.toByteArray(); + } catch (IOException e) { + // Workaround checked exception. + throw new RuntimeException(e); + } + } + + /** {@inheritDoc} */ + @Override + protected void setStateInternal(byte[] s) { + try { + final ByteArrayInputStream bis = new ByteArrayInputStream(s); + final ObjectInputStream ois = new ObjectInputStream(bis); + + delegate = (Random) ois.readObject(); + } catch (ClassNotFoundException|IOException e) { + // Workaround checked exception. + throw new RuntimeException(e); + } + } +}