Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
RNG-168: LXM family of random number generators
Add support for generators included in JDK 17: L32X64Mix L64X128StarStar L64X128Mix L64X256Mix L64X1024Mix L128X128Mix L128X256Mix L128X1024Mix Added benchmark for support routines for computing the unsigned long multiplications in the 128-bit LCG (linear congruential generator). Added a benchmark for the jump function to allow comparison with the equivalent base XBG (xor-based generator). Added a faster carry computation than the method provided in the reference LXM paper.
- Loading branch information
Showing
40 changed files
with
8,422 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -0,0 +1,216 @@ | ||
/* | ||
* 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.rng.core.source32; | ||
|
||
import org.apache.commons.rng.JumpableUniformRandomProvider; | ||
import org.apache.commons.rng.LongJumpableUniformRandomProvider; | ||
import org.apache.commons.rng.UniformRandomProvider; | ||
import org.apache.commons.rng.core.util.NumberFactory; | ||
|
||
/** | ||
* A 32-bit all purpose generator. | ||
* | ||
* <p>This is a member of the LXM family of generators: L=Linear congruential generator; | ||
* X=Xor based generator; and M=Mix. This member uses a 32-bit LCG and 64-bit Xor-based | ||
* generator. It is named as {@code "L32X64MixRandom"} in the {@code java.util.random} | ||
* package introduced in JDK 17; the LXM family is described in further detail in: | ||
* | ||
* <blockquote>Steele and Vigna (2021) LXM: better splittable pseudorandom number generators | ||
* (and almost as fast). Proceedings of the ACM on Programming Languages, Volume 5, | ||
* Article 148, pp 1–31.</blockquote> | ||
* | ||
* <p>Memory footprint is 128 bits and the period is 2<sup>32</sup> (2<sup>64</sup> - 1). | ||
* | ||
* <p>This generator implements {@link LongJumpableUniformRandomProvider}. | ||
* In addition instances created with a different additive parameter for the LCG are robust | ||
* against accidental correlation in a multi-threaded setting. The additive parameters must be | ||
* different in the most significant 31-bits. | ||
* | ||
* @see <a href="https://doi.org/10.1145/3485525">Steele & Vigna (2021) Proc. ACM Programming | ||
* Languages 5, 1-31</a> | ||
* @see <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/random/package-summary.html"> | ||
* JDK 17 java.util.random javadoc</a> | ||
* @since 1.5 | ||
*/ | ||
public final class L32X64Mix extends IntProvider implements LongJumpableUniformRandomProvider { | ||
// Implementation note: | ||
// This does not extend AbstractXoRoShiRo64 as the XBG function is re-implemented | ||
// inline to allow parallel pipelining. Inheritance would provide only the XBG state. | ||
|
||
/** LCG multiplier. */ | ||
private static final int M = LXMSupport.M32; | ||
/** Size of the state vector. */ | ||
private static final int SEED_SIZE = 4; | ||
|
||
/** Per-instance LCG additive parameter (must be odd). | ||
* Cannot be final to support RestorableUniformRandomProvider. */ | ||
private int la; | ||
/** State of the LCG generator. */ | ||
private int ls; | ||
/** State 0 of the XBG generator. */ | ||
private int x0; | ||
/** State 1 of the XBG generator. */ | ||
private int x1; | ||
|
||
/** | ||
* Creates a new instance. | ||
* | ||
* @param seed Initial seed. | ||
* If the length is larger than 4, only the first 4 elements will | ||
* be used; if smaller, the remaining elements will be automatically | ||
* set. A seed containing all zeros in the last two elements | ||
* will create a non-functional XBG sub-generator and a low | ||
* quality output with a period of 2<sup>32</sup>. | ||
* | ||
* <p>The 1st element is used to set the LCG increment; the least significant bit | ||
* is set to odd to ensure a full period LCG. The 2nd element is used | ||
* to set the LCG state.</p> | ||
*/ | ||
public L32X64Mix(int[] seed) { | ||
setState(extendSeed(seed, SEED_SIZE)); | ||
} | ||
|
||
/** | ||
* Creates a new instance using a 4 element seed. | ||
* A seed containing all zeros in the last two elements | ||
* will create a non-functional XBG sub-generator and a low | ||
* quality output with a period of 2<sup>32</sup>. | ||
* | ||
* <p>The 1st element is used to set the LCG increment; the least significant bit | ||
* is set to odd to ensure a full period LCG. The 2nd element is used | ||
* to set the LCG state.</p> | ||
* | ||
* @param seed0 Initial seed element 0. | ||
* @param seed1 Initial seed element 1. | ||
* @param seed2 Initial seed element 2. | ||
* @param seed3 Initial seed element 3. | ||
*/ | ||
public L32X64Mix(int seed0, int seed1, int seed2, int seed3) { | ||
// Additive parameter must be odd | ||
la = seed0 | 1; | ||
ls = seed1; | ||
x0 = seed2; | ||
x1 = seed3; | ||
} | ||
|
||
/** | ||
* Creates a copy instance. | ||
* | ||
* @param source Source to copy. | ||
*/ | ||
private L32X64Mix(L32X64Mix source) { | ||
super(source); | ||
la = source.la; | ||
ls = source.ls; | ||
x0 = source.x0; | ||
x1 = source.x1; | ||
} | ||
|
||
/** | ||
* Copies the state into the generator state. | ||
* | ||
* @param state the new state | ||
*/ | ||
private void setState(int[] state) { | ||
// Additive parameter must be odd | ||
la = state[0] | 1; | ||
ls = state[1]; | ||
x0 = state[2]; | ||
x1 = state[3]; | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override | ||
protected byte[] getStateInternal() { | ||
return composeStateInternal(NumberFactory.makeByteArray(new int[] {la, ls, x0, x1}), | ||
super.getStateInternal()); | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override | ||
protected void setStateInternal(byte[] s) { | ||
final byte[][] c = splitStateInternal(s, SEED_SIZE * Integer.BYTES); | ||
setState(NumberFactory.makeIntArray(c[0])); | ||
super.setStateInternal(c[1]); | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override | ||
public int next() { | ||
// LXM generate. | ||
// Old state is used for the output allowing parallel pipelining | ||
// on processors that support multiple concurrent instructions. | ||
|
||
final int s0 = x0; | ||
final int s = ls; | ||
|
||
// Mix | ||
final int z = LXMSupport.lea32(s + s0); | ||
|
||
// LCG update | ||
ls = M * s + la; | ||
|
||
// XBG update | ||
int s1 = x1; | ||
|
||
s1 ^= s0; | ||
x0 = Integer.rotateLeft(s0, 26) ^ s1 ^ (s1 << 9); // a, b | ||
x1 = Integer.rotateLeft(s1, 13); // c | ||
|
||
return z; | ||
} | ||
|
||
/** | ||
* Creates a copy of the UniformRandomProvider and then <em>retreats</em> the state of the | ||
* current instance. The copy is returned. | ||
* | ||
* <p>The jump is performed by advancing the state of the LCG sub-generator by 1 cycle. | ||
* The XBG state is unchanged. The jump size is the equivalent of moving the state | ||
* <em>backwards</em> by (2<sup>64</sup> - 1) positions. It can provide up to 2<sup>32</sup> | ||
* non-overlapping subsequences.</p> | ||
*/ | ||
@Override | ||
public UniformRandomProvider jump() { | ||
final UniformRandomProvider copy = new L32X64Mix(this); | ||
// Advance the LCG 1 step | ||
ls = M * ls + la; | ||
resetCachedState(); | ||
return copy; | ||
} | ||
|
||
/** | ||
* Creates a copy of the UniformRandomProvider and then <em>retreats</em> the state of the | ||
* current instance. The copy is returned. | ||
* | ||
* <p>The jump is performed by advancing the state of the LCG sub-generator by | ||
* 2<sup>16</sup> cycles. The XBG state is unchanged. The jump size is the equivalent | ||
* of moving the state <em>backwards</em> by 2<sup>16</sup> (2<sup>64</sup> - 1) | ||
* positions. It can provide up to 2<sup>16</sup> non-overlapping subsequences of | ||
* length 2<sup>16</sup> (2<sup>64</sup> - 1); each subsequence can provide up to | ||
* 2<sup>16</sup> non-overlapping subsequences of length (2<sup>64</sup> - 1) using | ||
* the {@link #jump()} method.</p> | ||
*/ | ||
@Override | ||
public JumpableUniformRandomProvider longJump() { | ||
final JumpableUniformRandomProvider copy = new L32X64Mix(this); | ||
// Advance the LCG 2^16 steps | ||
ls = LXMSupport.M32P * ls + LXMSupport.C32P * la; | ||
resetCachedState(); | ||
return copy; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -0,0 +1,82 @@ | ||
/* | ||
* 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.rng.core.source32; | ||
|
||
/** | ||
* Utility support for the LXM family of generators. The LXM family is described | ||
* in further detail in: | ||
* | ||
* <blockquote>Steele and Vigna (2021) LXM: better splittable pseudorandom number generators | ||
* (and almost as fast). Proceedings of the ACM on Programming Languages, Volume 5, | ||
* Article 148, pp 1–31.</blockquote> | ||
* | ||
* <p>Constants are provided to advance the state of an LCG by a power of 2 in a single | ||
* multiply operation to support jump operations. | ||
* | ||
* @see <a href="https://doi.org/10.1145/3485525">Steele & Vigna (2021) Proc. ACM Programming | ||
* Languages 5, 1-31</a> | ||
* @since 1.5 | ||
*/ | ||
final class LXMSupport { | ||
/** 32-bit LCG multiplier. Note: (M % 8) = 5. */ | ||
static final int M32 = 0xadb4a92d; | ||
/** Jump constant {@code m'} for an advance of the 32-bit LCG by 2^16. | ||
* Computed as: {@code m' = m^(2^16) (mod 2^32)}. */ | ||
static final int M32P = 0x65640001; | ||
/** Jump constant precursor for {@code c'} for an advance of the 32-bit LCG by 2^16. | ||
* Computed as: | ||
* <pre> | ||
* product_{i=0}^{15} { M^(2^i) + 1 } (mod 2^32) | ||
* </pre> | ||
* <p>The jump is computed for the LCG with an update step of {@code s = m * s + c} as: | ||
* <pre> | ||
* s = m' * s + c' * c | ||
* </pre> | ||
*/ | ||
static final int C32P = 0x046b0000; | ||
|
||
/** No instances. */ | ||
private LXMSupport() {} | ||
|
||
/** | ||
* Perform a 32-bit mixing function using Doug Lea's 32-bit mix constants and shifts. | ||
* | ||
* <p>This is based on the original 32-bit mix function of Austin Appleby's | ||
* MurmurHash3 modified to use a single mix constant and 16-bit shifts, which may have | ||
* a performance advantage on some processors. | ||
* | ||
* <p>The code was kindly provided by Guy Steele as a printing constraint led to | ||
* its omission from Steele and Vigna's paper. | ||
* | ||
* <p>Note from Guy Steele: | ||
* <blockquote> | ||
* The constant 0xd36d884b was chosen by Doug Lea by taking the (two’s-complement) | ||
* negation of the decimal constant 747796405, which appears in Table 5 of L’Ecuyer’s | ||
* classic paper “Tables of Linear Congruential Generators of Different Sizes and Good | ||
* Lattice Structure” (January 1999); the constant in lea64 was chosen in a similar manner. | ||
* These choices were based on his engineering intuition and then validated by testing. | ||
* </blockquote> | ||
* | ||
* @param x the input value | ||
* @return the output value | ||
*/ | ||
static int lea32(int x) { | ||
x = (x ^ (x >>> 16)) * 0xd36d884b; | ||
x = (x ^ (x >>> 16)) * 0xd36d884b; | ||
return x ^ (x >>> 16); | ||
} | ||
} |
Oops, something went wrong.