diff --git a/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/Philox4x32.java b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/Philox4x32.java new file mode 100644 index 000000000..bcfad35df --- /dev/null +++ b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/Philox4x32.java @@ -0,0 +1,325 @@ +/* + * 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; + +import java.util.Arrays; + +/** + * This class implements the Philox4x32 128-bit counter-based generator with 10 rounds. + * Jumping in the sequence is essentially instantaneous. This generator provides subsequences for easy parallelization. + * + * @see Parallel Random Numbers: As Easy as 1,2,3 + * @since 1.7 + */ +public final class Philox4x32 extends IntProvider implements LongJumpableUniformRandomProvider { + /** + * Philox 32-bit mixing constant for counter 0. + */ + private static final int K_PHILOX_10_A = 0x9E3779B9; + /** + * Philox 32-bit mixing constant for counter 1. + */ + private static final int K_PHILOX_10_B = 0xBB67AE85; + /** + * Philox 32-bit constant for key 0. + */ + private static final int K_PHILOX_SA = 0xD2511F53; + /** + * Philox 32-bit constant for key 1. + */ + private static final int K_PHILOX_SB = 0xCD9E8D57; + /** + * Internal buffer size. + */ + private static final int PHILOX_BUFFER_SIZE = 4; + /** + * number of int variables. + */ + private static final int STATE_SIZE = 7; + + /** + * Counter 0. + */ + private int counter0; + /** + * Counter 1. + */ + private int counter1; + /** + * Counter 2. + */ + private int counter2; + /** + * Counter 3. + */ + private int counter3; + /** + * Output point. + */ + private int[] buffer = new int[PHILOX_BUFFER_SIZE]; // UINT4 + /** + * Key low bits. + */ + private int key0; + /** + * Key high bits. + */ + private int key1; + /** + * State index: which output word is next (0..3). + */ + private int bufferPosition; + + + /** + * Copy constructor. + * + * @param source Source to copy. + */ + private Philox4x32(Philox4x32 source) { + super(source); + counter0 = source.counter0; + counter1 = source.counter1; + counter2 = source.counter2; + counter3 = source.counter3; + key0 = source.key0; + key1 = source.key1; + bufferPosition = source.bufferPosition; + buffer = source.buffer.clone(); + } + + /** + * Creates a new instance with default seed. Subsequence and offset (or equivalently, the internal counter) + * are set to zero. + */ + public Philox4x32() { + this(67280421310721L); + } + + /** + * Creates a new instance with a given seed. Subsequence and offset (or equivalently, the internal counter) + * are set to zero. + * + * @param key the low 32 bits constitute the first int key of Philox, + * and the high 32 bits constitute the second int key of Philox + */ + private Philox4x32(long key) { + this(new int[]{(int) key, (int) (key >>> 32)}); + } + + /** + * Creates a new instance based on an array of int containing, key (first two ints) and + * the counter (next 4 ints, low bits = first int). The counter is not scrambled and may + * be used to create contiguous blocks with size a multiple of 4 ints. For example, + * setting seed[2] = 1 is equivalent to start with seed[2]=0 and calling {@link #next()} 4 times. + * + * @param seed an array of size 6 defining key0,key1,counter0,counter1,counter2,counter3. + * If the size is smaller, zero values are assumed. + */ + public Philox4x32(int[] seed) { + final int[] input = seed.length < 6 ? Arrays.copyOf(seed, 6) : seed; + key0 = input[0]; + key1 = input[1]; + counter0 = input[2]; + counter1 = input[3]; + counter2 = input[4]; + counter3 = input[5]; + bufferPosition = PHILOX_BUFFER_SIZE; + } + + /** + * Fetch next integer from the buffer, or regenerate the buffer using 10 rounds. + * + * @return random integer + */ + @Override + public int next() { + final int p = bufferPosition; + if (p < PHILOX_BUFFER_SIZE) { + bufferPosition = p + 1; + return buffer[p]; + } + incrementCounter(); + rand10(); + bufferPosition = 1; + return buffer[0]; + } + + /** + * Increment by one. + */ + private void incrementCounter() { + counter0++; + if (counter0 != 0) { + return; + } + + counter1++; + if (counter1 != 0) { + return; + } + + counter2++; + if (counter2 != 0) { + return; + } + + counter3++; + } + + /** + * Performs a single round of philox. + * + * @param ctr local counter, which will be updated after each call. + * @param key0 key low bits + * @param key1 key high bits + */ + private static void singleRound(int[] ctr, int key0, int key1) { + long product = (K_PHILOX_SA & 0xFFFFFFFFL) * (ctr[0] & 0xFFFFFFFFL); + final int hi0 = (int) (product >>> 32); + final int lo0 = (int) product; + product = (K_PHILOX_SB & 0xFFFFFFFFL) * (ctr[2] & 0xFFFFFFFFL); + final int hi1 = (int) (product >>> 32); + final int lo1 = (int) product; + + ctr[0] = hi1 ^ ctr[1] ^ key0; + ctr[1] = lo1; + ctr[2] = hi0 ^ ctr[3] ^ key1; + ctr[3] = lo0; + } + + /** + * Perform 10 rounds, using counter0, counter1, counter2, counter3 as starting point. + * It updates the buffer member variable, but no others. + */ + private void rand10() { + buffer[0] = counter0; + buffer[1] = counter1; + buffer[2] = counter2; + buffer[3] = counter3; + + int k0 = key0; + int k1 = key1; + + //unrolled loop for performance + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + } + + + /** + * {@inheritDoc} + * + *

Increments the subsequence by 1.

+ *

The jump size is the equivalent of 4*296 calls to + * {@link UniformRandomProvider#nextInt() nextInt()}. + */ + @Override + public JumpableUniformRandomProvider longJump() { + final Philox4x32 copy = copy(); + counter3++; + rand10(); + resetCachedState(); + return copy; + } + + /** + * {@inheritDoc} + * + *

The jump size is the equivalent of 4*264 + * calls to {@link UniformRandomProvider#nextInt() nextInt()}. + */ + @Override + public UniformRandomProvider jump() { + final Philox4x32 copy = copy(); + if (++counter2 == 0) { + counter3++; + } + rand10(); + resetCachedState(); + return copy; + } + + /** + * {@inheritDoc} + */ + @Override + protected byte[] getStateInternal() { + return composeStateInternal(NumberFactory.makeByteArray( + new int[]{key0, key1, counter0, counter1, counter2, counter3, bufferPosition}), + super.getStateInternal()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void setStateInternal(byte[] s) { + final byte[][] c = splitStateInternal(s, STATE_SIZE * 4); + final int[] state = NumberFactory.makeIntArray(c[0]); + key0 = state[0]; + key1 = state[1]; + counter0 = state[2]; + counter1 = state[3]; + counter2 = state[4]; + counter3 = state[5]; + bufferPosition = state[6]; + super.setStateInternal(c[1]); + rand10(); //to regenerate the internal buffer + } + + /** + * Create a copy. + * + * @return the copy + */ + private Philox4x32 copy() { + return new Philox4x32(this); + } +} diff --git a/commons-rng-core/src/main/java/org/apache/commons/rng/core/source64/Philox4x64.java b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source64/Philox4x64.java new file mode 100644 index 000000000..8cb15e1c0 --- /dev/null +++ b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source64/Philox4x64.java @@ -0,0 +1,319 @@ +/* + * 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.source64; + +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; + +import java.util.Arrays; + +/** + * This class implements the Philox4x64 256-bit counter-based generator with 10 rounds. + * Jumping in the sequence is essentially instantaneous. This generator provides subsequences for easy parallelization. + * + * @see Parallel Random Numbers: As Easy as 1,2,3 + * @since 1.7 + */ +public final class Philox4x64 extends LongProvider implements LongJumpableUniformRandomProvider { + /** + * Philox 32-bit mixing constant for counter 0. + */ + private static final long PHILOX_M0 = 0xD2E7470EE14C6C93L; + /** + * Philox 32-bit mixing constant for counter 1. + */ + private static final long PHILOX_M1 = 0xCA5A826395121157L; + /** + * Philox 32-bit constant for key 0. + */ + private static final long PHILOX_W0 = 0x9E3779B97F4A7C15L; + /** + * Philox 32-bit constant for key 1. + */ + private static final long PHILOX_W1 = 0xBB67AE8584CAA73BL; + /** + * Internal buffer size. + */ + private static final int PHILOX_BUFFER_SIZE = 4; + /** + * number of long variables. + */ + private static final int STATE_SIZE = 7; + + /** + * Counter 0. + */ + private long counter0; + /** + * Counter 1. + */ + private long counter1; + /** + * Counter 2. + */ + private long counter2; + /** + * Counter 3. + */ + private long counter3; + + /** + * Output point. + */ + private long[] buffer = new long[PHILOX_BUFFER_SIZE]; // UINT4 + /** + * Key low bits. + */ + private long key0; + /** + * Key high bits. + */ + private long key1; + /** + * State index: which output word is next (0..3). + */ + private int bufferPosition; + + /** + * Copy constructor. + * + * @param source Source to copy. + */ + private Philox4x64(Philox4x64 source) { + super(source); + counter0 = source.counter0; + counter1 = source.counter1; + counter2 = source.counter2; + counter3 = source.counter3; + key0 = source.key0; + key1 = source.key1; + bufferPosition = source.bufferPosition; + buffer = source.buffer.clone(); + } + + /** + * Creates a new instance with default seed. Subsequence and offset are set to zero. + */ + public Philox4x64() { + this(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0L, 0L, 0L, 0L}); + } + + + /** + * Creates a new instance given 6 long numbers containing, key (first two longs) and + * the counter (next 4, starts at first). The counter is not scrambled and may + * be used to create contiguous blocks with size a multiple of 4 longs. For example, + * setting seed[2] = 1 is equivalent to start with seed[2]=0 and calling {@link #next()} 4 times. + * + * @param keyAndCounter the first two number are the key and the next 4 number are the counter. + * if size is smaller than 6, the array is padded with 0. + */ + public Philox4x64(long[] keyAndCounter) { + final long[] input = keyAndCounter.length < 6 ? Arrays.copyOf(keyAndCounter, 6) : keyAndCounter; + key0 = input[0]; + key1 = input[1]; + counter0 = input[2]; + counter1 = input[3]; + counter2 = input[4]; + counter3 = input[5]; + bufferPosition = PHILOX_BUFFER_SIZE; + } + + /** + * Fetch next long from the buffer, or regenerate the buffer using 10 rounds. + * + * @return random 64-bit integer + */ + private long next64() { + final int p = bufferPosition; + if (bufferPosition < PHILOX_BUFFER_SIZE) { + bufferPosition = p + 1; + return buffer[p]; + } + incrementCounter(); + rand10(); + bufferPosition = 1; + return buffer[0]; + } + + /** + * Increment by one. + */ + private void incrementCounter() { + counter0++; + if (counter0 != 0) { + return; + } + + counter1++; + if (counter1 != 0) { + return; + } + + counter2++; + if (counter2 != 0) { + return; + } + + counter3++; + } + + /** + * Performs a single round of philox. + * + * @param counter local counter, which will be updated after each call. + * @param key0 key low bits + * @param key1 key high bits + */ + private static void singleRound(long[] counter, long key0, long key1) { + final long lo0 = PHILOX_M0 * counter[0]; + final long hi0 = LXMSupport.unsignedMultiplyHigh(PHILOX_M0, counter[0]); + final long lo1 = PHILOX_M1 * counter[2]; + final long hi1 = LXMSupport.unsignedMultiplyHigh(PHILOX_M1, counter[2]); + + counter[0] = hi1 ^ counter[1] ^ key0; + counter[1] = lo1; + counter[2] = hi0 ^ counter[3] ^ key1; + counter[3] = lo0; + } + + /** + * Perform 10 rounds, using counter0, counter1, counter2, counter3 as starting point. + * + */ + private void rand10() { + buffer[0] = counter0; + buffer[1] = counter1; + buffer[2] = counter2; + buffer[3] = counter3; + + long k0 = key0; + long k1 = key1; + + //unrolled loop for performance + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + } + + /** + * {@inheritDoc} + * + *

Increments the subsequence by 1.

+ *

The jump size is the equivalent of 4*2192 calls to + * {@link UniformRandomProvider#nextLong() nextLong()}. + */ + @Override + public JumpableUniformRandomProvider longJump() { + final Philox4x64 copy = copy(); + counter3++; + rand10(); + resetCachedState(); + return copy; + } + + /** + * {@inheritDoc} + * + *

The jump size is the equivalent of 4*2128 + * calls to {@link UniformRandomProvider#nextLong() nextLong()}. + */ + @Override + public UniformRandomProvider jump() { + final Philox4x64 copy = copy(); + if (++counter2 == 0) { + counter3++; + } + rand10(); + resetCachedState(); + return copy; + } + + /** + * {@inheritDoc} + */ + @Override + public long next() { + return next64(); + } + + /** + * {@inheritDoc} + */ + @Override + protected byte[] getStateInternal() { + return composeStateInternal(NumberFactory.makeByteArray( + new long[]{key0, key1, counter0, counter1, counter2, counter3, bufferPosition}), + super.getStateInternal()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void setStateInternal(byte[] s) { + final byte[][] c = splitStateInternal(s, STATE_SIZE * 8); + + final long[] state = NumberFactory.makeLongArray(c[0]); + key0 = state[0]; + key1 = state[1]; + counter0 = state[2]; + counter1 = state[3]; + counter2 = state[4]; + counter3 = state[5]; + bufferPosition = (int) state[6]; + super.setStateInternal(c[1]); + rand10(); + } + + /** + * Create a copy. + * + * @return the copy + */ + private Philox4x64 copy() { + return new Philox4x64(this); + } +} diff --git a/commons-rng-core/src/test/java/org/apache/commons/rng/core/ProvidersList.java b/commons-rng-core/src/test/java/org/apache/commons/rng/core/ProvidersList.java index 04faee99c..47b93928f 100644 --- a/commons-rng-core/src/test/java/org/apache/commons/rng/core/ProvidersList.java +++ b/commons-rng-core/src/test/java/org/apache/commons/rng/core/ProvidersList.java @@ -23,6 +23,7 @@ import org.apache.commons.rng.core.source32.JDKRandom; import org.apache.commons.rng.core.source32.JenkinsSmallFast32; +import org.apache.commons.rng.core.source32.Philox4x32; import org.apache.commons.rng.core.source32.Well512a; import org.apache.commons.rng.core.source32.XoRoShiRo64Star; import org.apache.commons.rng.core.source32.XoRoShiRo64StarStar; @@ -45,6 +46,7 @@ import org.apache.commons.rng.core.source32.DotyHumphreySmallFastCounting32; import org.apache.commons.rng.core.source32.PcgMcgXshRr32; import org.apache.commons.rng.core.source32.PcgMcgXshRs32; +import org.apache.commons.rng.core.source64.Philox4x64; import org.apache.commons.rng.core.source64.SplitMix64; import org.apache.commons.rng.core.source64.XorShift1024Star; import org.apache.commons.rng.core.source64.XorShift1024StarPhi; @@ -130,6 +132,7 @@ public final class ProvidersList { LIST32.add(new JenkinsSmallFast32(g.nextInt())); LIST32.add(new XoShiRo128PlusPlus(new int[] {g.nextInt(), g.nextInt(), g.nextInt()})); LIST32.add(new L32X64Mix(new int[] {g.nextInt(), g.nextInt()})); + LIST32.add(new Philox4x32(new int[] {g.nextInt(), g.nextInt()})); // ... add more here. // "long"-based RNGs. @@ -163,6 +166,7 @@ public final class ProvidersList { LIST64.add(new L128X128Mix(new long[] {g.nextLong(), g.nextLong(), g.nextLong(), g.nextLong()})); LIST64.add(new L128X256Mix(new long[] {g.nextLong(), g.nextLong(), g.nextLong(), g.nextLong()})); LIST64.add(new L128X1024Mix(new long[] {g.nextLong(), g.nextLong(), g.nextLong(), g.nextLong()})); + LIST64.add(new Philox4x64(new long[] {g.nextLong(), g.nextLong()})); // ... add more here. // Do not modify the remaining statements. diff --git a/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/Philox4x32Test.java b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/Philox4x32Test.java new file mode 100644 index 000000000..1f19c78ba --- /dev/null +++ b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/Philox4x32Test.java @@ -0,0 +1,188 @@ +/* + * 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.core.RandomAssert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class Philox4x32Test { + + /* + * Data from python randomgen.philox.Philox(key=1234,number=4,width=32) random_raw() + * https://bashtage.github.io/randomgen/bit_generators/philox.html + */ + + private static final int[] EXPECTED_SEQUENCE_1234 = { + -1628512715, 482218876, -98078573, 343858512, 1070188760, + -66651592, -870905049, -1994573039, -1238984130, 599211371, + 1926069095, -394512546, 346514135, -352142790, 196394741, + -107436867, -903274039, 860026475, -1309487194, -1778049224, + -49503714, -1441076994, -866074276, -1339523817, -1290919251, + 1857369626, -1839251177, -2041498882, -1956330288, 905306810, + -2114083635, 200746399, 20291031, 214040874, -1628891823, + -1958807646, 9198301, -1607720479, -1349496224, 1418271217 + }; + + private static final int[] EXPECTED_SEQUENCE_DEFAULT = { + 623720234, -686991347, 358698524, 234508473, 1303720625, + 1235930736, -75297729, 110380616, 829652807, -1101240720, + -1443748750, -1366075136, -1702811520, 232450464, 350957237, + 1425642103, 256542391, 1837662153, -448554748, 637025846, + -902021712, 1085962074, -1391041963, 201580325, 1416828610, + 599210676, -628463662, -576572235, 457140358, -1026551805, + -917125498, 529387774, 1254882949, 1278069784, 724938314, + -4044975, -1211844829, -198846304, 286548119, 2085574084 + }; + + private static final int[] EXPECTED_SEQUENCE_AFTER_JUMP = { + -1941342745, 535234737, -1560986946, 1333403881, -467630828, + -1212243215, 1924495835, 1889500660, 118588722, -444471278, + -984974572, 2134204567, 620921081, -929199568, -44345645, + -346841340, -557091335, 1023562906, -1544843001, 2014718360, + -186712859, -874952234, -1016908504, 953606755, -1406346322, + -1297454974, 1426742334, 1461035068, 206733349, 1606578263, + -1354963004, -604654637, 782017623, 1501746828, 853947605, + -1380277812, 1855551741, -1023933348, -635058958, 1752530776 + }; + + private static final int[] EXPECTED_SEQUENCE_AFTER_LONG_JUMP = { + -643973534, -1464631510, -1204127809, 380399830, 1336312468, + 862647039, -970571153, -1473390944, 811398823, -598244991, + -1474151641, -1228756553, -166611808, -231601273, -2055417682, + -1102476522, 1497124960, 438167652, -657449781, -404513325, + -621271837, -10198296, -267651022, -296539606, -1564719261, + -652626768, -973911394, 1388361366, 1675611708, -1270745165, + -620748722, -1569788343, 831908952, 1873081673, -1058521087, + -26171115, -1211556401, -65210719, -1194284085, 1579466740 + }; + + /** + * Gets a stream of reference data. Each argument consists of the seed as a long (first two ints), + * and the int array of the expected output from the generator. + * + * @return the reference data + */ + Stream getReferenceData() { + return Stream.of( + Arguments.of(1234L, EXPECTED_SEQUENCE_1234), + Arguments.of(67280421310721L, EXPECTED_SEQUENCE_DEFAULT) + ); + } + + @ParameterizedTest + @MethodSource(value = "getReferenceData") + void testReferenceCode(long seed, int[] expected) { + RandomAssert.assertEquals(expected, new Philox4x32(new int[]{(int) seed, (int) (seed >>> 32)})); + } + + @Test + void testConstructors() { + Philox4x32[] rngs = new Philox4x32[]{ + new Philox4x32(), + new Philox4x32(new int[]{(int) 67280421310721L, (int) (67280421310721L >>> 32), 0, 0, 0, 0}) + }; + int refValue = rngs[0].next(); + for (int i = 1; i < rngs.length; i++) { + int value = rngs[i].next(); + assertEquals(refValue, value, "Philox4x32 initialization for i=" + i); + } + rngs = new Philox4x32[]{ + new Philox4x32(new int[]{1234, 0, 1}), + new Philox4x32(new int[]{1234, 0, 1, 0, 0, 0}), + }; + refValue = rngs[0].next(); + for (int i = 1; i < rngs.length; i++) { + int value = rngs[i].next(); + assertEquals(refValue, value, "Philox4x32 initialization for i=" + i); + } + rngs = new Philox4x32[]{ + new Philox4x32(new int[]{1234}), + new Philox4x32(new int[]{1234, 0}), + new Philox4x32(new int[]{1234, 0, 0}), + new Philox4x32(new int[]{1234, 0, 0, 0}), + new Philox4x32(new int[]{1234, 0, 0, 0, 0}), + new Philox4x32(new int[]{1234, 0, 0, 0, 0, 0}), + new Philox4x32(new int[]{1234, 0, 0, 0, 0, 0, 0}), + }; + refValue = rngs[0].next(); + for (int i = 1; i < rngs.length; i++) { + int value = rngs[i].next(); + assertEquals(refValue, value, "Philox4x32 initialization for i=" + i); + } + } + + @Test + void testJump() { + RandomAssert.assertJumpEquals(EXPECTED_SEQUENCE_DEFAULT, EXPECTED_SEQUENCE_AFTER_JUMP, new Philox4x32()); + } + + @Test + void testLongJump() { + RandomAssert.assertLongJumpEquals(EXPECTED_SEQUENCE_DEFAULT, EXPECTED_SEQUENCE_AFTER_LONG_JUMP, new Philox4x32()); + } + + @Test + void testInternalCounter() { + //test of incrementCounter + Philox4x32 rng = new Philox4x32(new int[]{(int) 67280421310721L, (int) (67280421310721L >>> 32), 0xffffffff, 0, 0, 0}); + for (int i = 0; i < 4; i++) { + rng.next(); + } + Philox4x32 rng2 = new Philox4x32(new int[]{(int) 67280421310721L, (int) (67280421310721L >>> 32), 0, 1, 0, 0}); + RandomAssert.assertNextIntEquals(1, rng, rng2); + + rng = new Philox4x32(new int[]{(int) 67280421310721L, (int) (67280421310721L >>> 32), 0xffffffff, 0xffffffff, 0, 0}); + for (int i = 0; i < 4; i++) { + rng.next(); + } + rng2 = new Philox4x32(new int[]{(int) 67280421310721L, (int) (67280421310721L >>> 32), 0, 0, 1, 0}); + RandomAssert.assertNextIntEquals(1, rng, rng2); + + rng = new Philox4x32(new int[]{(int) 67280421310721L, (int) (67280421310721L >>> 32), 0xffffffff, 0xffffffff, 0xffffffff, 0}); + for (int i = 0; i < 4; i++) { + rng.next(); + } + rng2 = new Philox4x32(new int[]{(int) 67280421310721L, (int) (67280421310721L >>> 32), 0, 0, 0, 1}); + RandomAssert.assertNextIntEquals(1, rng, rng2); + } + + @Test + void testLongJumpCounter() { + Philox4x32 rng = new Philox4x32(new int[]{1234, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0}); + rng.longJump(); + Philox4x32 rng2 = new Philox4x32(new int[]{1234, 0, 0xffffffff, 0xffffffff, 0xffffffff, 1}); + RandomAssert.assertNextIntEquals(1, rng, rng2); + } + + @Test + void testJumpCounter() { + Philox4x32 rng = new Philox4x32(new int[]{1234, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0}); + rng.jump(); + Philox4x32 rng2 = new Philox4x32(new int[]{1234, 0, 0xffffffff, 0xffffffff, 0, 1}); + RandomAssert.assertNextIntEquals(1, rng, rng2); + } + +} diff --git a/commons-rng-core/src/test/java/org/apache/commons/rng/core/source64/Philox4x64Test.java b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source64/Philox4x64Test.java new file mode 100644 index 000000000..40523bbc7 --- /dev/null +++ b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source64/Philox4x64Test.java @@ -0,0 +1,205 @@ +/* + * 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.source64; + +import org.apache.commons.rng.core.RandomAssert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class Philox4x64Test { + /* + * Data from python randomgen.philox.Philox(key=1234,number=4,width=32) random_raw() + * https://bashtage.github.io/randomgen/bit_generators/philox.html + */ + + private static final long[] EXPECTED_SEQUENCE_1234 = { + 6174562084317992592L, -7568142518571726206L, -5685918792241859306L, + 6151287208724416091L, -7525285015497232737L, -2526119061336846091L, + -2093373494943999176L, 2505686065164099867L, 1493954073060533072L, + 2386252059344830309L, -3981277096068706128L, 4825385527958964709L, + 5896359280427319232L, 2130638389021018825L, 1001529696243618836L, + 6229771985419955916L, -8030183820248387325L, 5924921954534026109L, + -2430661683740471500L, -7119094164204651921L, 2451935767711287279L, + 8424479353221384040L, -5011970289299902244L, 8792348508803652203L, + 9109768561113011588L, 24126314432238277L, -8946976403367747978L, + 6224712922535513938L, 8733921062828259483L, 3855129282970288492L, + -15371244630355388L, -3103082637265535013L, -5696416329331263984L, + -5000982493478729316L, -3077201427991874994L, 4502749081228919907L, + 1930363720599024367L, -7884649763770700010L, 9162677665382083018L, + -1491083349895074892L + }; + + private static final long[] EXPECTED_SEQUENCE_DEFAULT = { + 7651105821017786633L, -986727441099762072L, -1758232618730818806L, + -6892647654339096064L, 2003912625120555464L, 847995992558080923L, + 2561190448322591348L, 5089323078274549892L, -6215224099279536444L, + 2839273132443259286L, -1538091565590055595L, 2262400997606952131L, + 4794890345824897152L, 2654554423835782039L, 5232844452212050618L, + 4968309811735346778L, -6677562093502275256L, -2345486924693103657L, + 2546479265789531422L, 1397198500311783458L, -3029924206687987745L, + 3915450377326980183L, -1798629713529533718L, 7813856890368443409L, + -7530219763187390588L, 7752320264114599504L, 4497386005519180400L, + 8983526426341050924L, 3157770966203722859L, 6531619948763639990L, + -2561361262383382379L, -7341089376366770572L, 5588349311041971766L, + -5547961913507498237L, 557535079196835645L, -7564858493373145745L, + -5687482083658299050L, -6040393957990987713L, 3376696212464637986L, + -4460669316800568753L + }; + + private static final long[] EXPECTED_SEQUENCE_AFTER_JUMP = { + -8246258822028745854L, -8108712508154901372L, 2654408923912105318L, + -6418747939867464899L, 8695124057120477387L, -4062778777418832523L, + -2866609061902870832L, -1985485291750970064L, -3716513824860297891L, + 2708966168515282018L, -8441862438570589384L, -3332504021977608920L, + 8275431876927502767L, -37683753608778224L, 4850475723424833501L, + -2864632267522668999L, -6547048909303846355L, -6804759155034193445L, + -1607076952104749058L, 7993605125443204784L, 7601442483044023354L, + -7379694727972198096L, -1902536664833944445L, -908773878773086264L, + -7367142976738044337L, 2845297286559921499L, 5398165976383543580L, + 2574122219286874876L, 3780790808954139828L, -7038343169285503987L, + 1381442423564430946L, -4910467881295472851L, 839863310680617535L, + 3700507604505113976L, 2586645934793105407L, 1058068213122536369L, + -1876209807038423750L, 8994121856634859944L, 4145729862086221315L, + -7214331765643557828L + }; + + private static final long[] EXPECTED_SEQUENCE_AFTER_LONG_JUMP = { + 234199833207670492L, 4847236961490835302L, 4652995647109309910L, + -3737386356448340712L, -5273383760715124519L, -3647957810120825499L, + 5146817817305263920L, 5710973906845063179L, -1479449555641285865L, + 4084674574582715314L, -5547600708256898652L, -4421640461296589483L, + -2968992335347510287L, -4790862279320238050L, -2473190691392812606L, + 965983568262991078L, 601327440871821012L, 8223565539892887311L, + 7546441310634873026L, 2825517271552261878L, 1821450327999942380L, + 1829354945050293158L, -4141883204296663957L, 2272925410140103105L, + 6950466720264053689L, 942049061182241074L, -423320710605977014L, + -7153892430601162036L, -3577327671114607603L, 2251213489013696162L, + -869366985991136417L, 6210870867759981069L, 8104504070499194349L, + -5828300645374305433L, -8988635423527025878L, 2037830179166981888L, + 600555068878135939L, -1046966376945680441L, 9153700819137910983L, + 6246833740445288808L + }; + + /** + * Gets a stream of reference data. Each argument consists of the seed as a long array (first two longs), + * and the long array of the expected output from the generator. + * + * @return the reference data + */ + Stream getReferenceData() { + return Stream.of( + Arguments.of(new long[]{1234L, 0}, EXPECTED_SEQUENCE_1234), + Arguments.of(new long[]{67280421310721L, 0x9E3779B97F4A7C15L}, EXPECTED_SEQUENCE_DEFAULT) + ); + } + + @ParameterizedTest + @MethodSource(value = "getReferenceData") + void testReferenceCode(long[] seed, long[] expected) { + RandomAssert.assertEquals(expected, new Philox4x64(seed)); + } + + @Test + void testJump() { + RandomAssert.assertJumpEquals(EXPECTED_SEQUENCE_DEFAULT, EXPECTED_SEQUENCE_AFTER_JUMP, + new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L})); + } + + @Test + void testLongJump() { + RandomAssert.assertLongJumpEquals(EXPECTED_SEQUENCE_DEFAULT, EXPECTED_SEQUENCE_AFTER_LONG_JUMP, new Philox4x64()); + } + + @Test + void testInternalCounter() { + //test of incrementCounter + Philox4x64 rng = new Philox4x64(new long[]{67280421310721L, 1234L, 0xffffffffffffffffL, 0, 0, 0}); + for (int i = 0; i < 4; i++) { + rng.next(); + } + Philox4x64 rng2 = new Philox4x64(new long[]{67280421310721L, 1234L, 0, 1, 0, 0}); + RandomAssert.assertNextLongEquals(1, rng, rng2); + + rng = new Philox4x64(new long[]{67280421310721L, 1234L, 0xffffffffffffffffL, 0xffffffffffffffffL, 0, 0}); + for (int i = 0; i < 4; i++) { + rng.next(); + } + rng2 = new Philox4x64(new long[]{67280421310721L, 1234L, 0, 0, 1, 0}); + RandomAssert.assertNextLongEquals(1, rng, rng2); + + rng = new Philox4x64(new long[]{67280421310721L, 1234L, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0}); + for (int i = 0; i < 4; i++) { + rng.next(); + } + rng2 = new Philox4x64(new long[]{67280421310721L, 1234L, 0, 0, 0, 1}); + RandomAssert.assertNextLongEquals(1, rng, rng2); + } + + @Test + void testLongJumpCounter() { + Philox4x64 rng = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 0, 0xffffffffffffffffL, 0}); + rng.jump(); + Philox4x64 rng2 = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 0, 0, 1}); + RandomAssert.assertNextLongEquals(1, rng, rng2); + + rng = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0}); + rng.jump(); + rng2 = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 0xffffffffffffffffL, 0, 1}); + RandomAssert.assertNextLongEquals(1, rng, rng2); + + rng = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0}); + rng.longJump(); + rng2 = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 1}); + RandomAssert.assertNextLongEquals(1, rng, rng2); + } + + @Test + void testConstructors() { + Philox4x64[] rngs = new Philox4x64[]{ + new Philox4x64(), + new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0, 0, 0, 0}), + new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L}), + new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0, 0, 0}), + new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0, 0}), + new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0}) + }; + long refValue = rngs[0].next(); + for (int i = 1; i < rngs.length; i++) { + long value = rngs[i].next(); + assertEquals(refValue, value, "Philox4x64 initialization for i=" + i); + } + + rngs = new Philox4x64[]{ + new Philox4x64(new long[]{1234L, 0, 1, 0}), + new Philox4x64(new long[]{1234, 0, 1}), + new Philox4x64(new long[]{1234, 0, 1, 0, 0, 0}), + }; + refValue = rngs[0].next(); + for (int i = 1; i < rngs.length; i++) { + long value = rngs[i].next(); + assertEquals(refValue, value, "Philox4x32 initialization for i=" + i); + } + } +} diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/RandomSourceValues.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/RandomSourceValues.java index 5b0bc5b0f..0d475d769 100644 --- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/RandomSourceValues.java +++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/RandomSourceValues.java @@ -70,6 +70,8 @@ public class RandomSourceValues { "PCG_RXS_M_XS_64", "PCG_MCG_XSH_RR_32", "PCG_MCG_XSH_RS_32", + "PHILOX_4X32", + "PHILOX_4X64", "MSWS", "SFC_32", "SFC_64", diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/BaselineSources.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/BaselineSources.java index e3b21776a..23b850cb9 100644 --- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/BaselineSources.java +++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/BaselineSources.java @@ -62,6 +62,8 @@ public abstract class BaselineSources { */ @Param({BASELINE, "JDK", + "PHILOX_4X32", + "PHILOX_4X64", "WELL_512_A", "WELL_1024_A", "WELL_19937_A", diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/JumpBenchmark.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/JumpBenchmark.java index 1740bd208..2d005f7d7 100644 --- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/JumpBenchmark.java +++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/JumpBenchmark.java @@ -116,7 +116,9 @@ public static class JumpableSource extends BaseJumpableSource { "L128_X128_MIX", "L128_X256_MIX", "L128_X1024_MIX", - "L32_X64_MIX"}) + "L32_X64_MIX", + "PHILOX_4X32", + "PHILOX_4X64"}) private String randomSourceName; /** {@inheritDoc} */ @@ -163,7 +165,9 @@ public static class LongJumpableSource extends BaseJumpableSource { "L128_X1024_MIX", // Requires the LCG to be advanced 2^16 rather than 1 cycle which // can use precomputed coefficients. - "L32_X64_MIX"}) + "L32_X64_MIX", + "PHILOX_4X32", + "PHILOX_4X64"}) private String randomSourceName; diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/NextDoubleGenerationPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/NextDoubleGenerationPerformance.java index a474ffc25..545ae1991 100644 --- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/NextDoubleGenerationPerformance.java +++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/NextDoubleGenerationPerformance.java @@ -21,7 +21,6 @@ import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; - /** * Executes benchmark to compare the speed of generation of random numbers from the * various source providers for {@link UniformRandomProvider#nextDouble()}. diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ConstructionPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ConstructionPerformance.java index ac912d857..0cdcf6790 100644 --- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ConstructionPerformance.java +++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ConstructionPerformance.java @@ -42,6 +42,7 @@ import org.apache.commons.rng.core.source32.KISSRandom; import org.apache.commons.rng.core.source32.MersenneTwister; import org.apache.commons.rng.core.source32.MultiplyWithCarry256; +import org.apache.commons.rng.core.source32.Philox4x32; import org.apache.commons.rng.core.source32.Well1024a; import org.apache.commons.rng.core.source32.Well19937a; import org.apache.commons.rng.core.source32.Well19937c; @@ -53,6 +54,7 @@ import org.apache.commons.rng.core.source32.XoShiRo128Plus; import org.apache.commons.rng.core.source32.XoShiRo128StarStar; import org.apache.commons.rng.core.source64.MersenneTwister64; +import org.apache.commons.rng.core.source64.Philox4x64; import org.apache.commons.rng.core.source64.SplitMix64; import org.apache.commons.rng.core.source64.TwoCmres; import org.apache.commons.rng.core.source64.XoRoShiRo128Plus; @@ -301,6 +303,7 @@ private static Object[] findNativeSeeds(RandomSource randomSource) { case XO_RO_SHI_RO_64_SS: case XO_SHI_RO_128_PLUS: case XO_SHI_RO_128_SS: + case PHILOX_4X32: return INT_ARRAY_SEEDS; case XOR_SHIFT_1024_S: case XOR_SHIFT_1024_S_PHI: @@ -311,6 +314,7 @@ private static Object[] findNativeSeeds(RandomSource randomSource) { case XO_SHI_RO_256_SS: case XO_SHI_RO_512_PLUS: case XO_SHI_RO_512_SS: + case PHILOX_4X64: return LONG_ARRAY_SEEDS; default: throw new AssertionError("Unknown native seed"); @@ -368,6 +372,9 @@ private static int findNativeSeedLength(RandomSource randomSource) { case XO_SHI_RO_512_PLUS: case XO_SHI_RO_512_SS: return 8; + case PHILOX_4X32: + case PHILOX_4X64: + return 6; default: throw new AssertionError("Unknown native seed size"); } @@ -398,6 +405,7 @@ private static int findNativeSeedElementByteSize(RandomSource randomSource) { case XO_RO_SHI_RO_64_SS: case XO_SHI_RO_128_PLUS: case XO_SHI_RO_128_SS: + case PHILOX_4X32: return 4; // int case SPLIT_MIX_64: case XOR_SHIFT_1024_S: @@ -409,6 +417,7 @@ private static int findNativeSeedElementByteSize(RandomSource randomSource) { case XO_SHI_RO_256_SS: case XO_SHI_RO_512_PLUS: case XO_SHI_RO_512_SS: + case PHILOX_4X64: return 8; // long default: throw new AssertionError("Unknown native seed element byte size"); @@ -450,6 +459,8 @@ private static RandomSourceInternal getRandomSourceInternal(RandomSource randomS case XO_SHI_RO_256_SS: return RandomSourceInternal.XO_SHI_RO_256_SS; case XO_SHI_RO_512_PLUS: return RandomSourceInternal.XO_SHI_RO_512_PLUS; case XO_SHI_RO_512_SS: return RandomSourceInternal.XO_SHI_RO_512_SS; + case PHILOX_4X32: return RandomSourceInternal.PHILOX_4X32; + case PHILOX_4X64: return RandomSourceInternal.PHILOX_4X64; default: throw new AssertionError("Unknown random source internal"); } @@ -793,6 +804,26 @@ public void newXoShiRo512StarStar(Blackhole bh) { } } + /** + * @param bh Data sink. + */ + @Benchmark + public void newPhilox4x32(Blackhole bh) { + for (int i = 0; i < SEEDS; i++) { + bh.consume(new Philox4x32(INT_ARRAY_SEEDS[i])); + } + } + + /** + * @param bh Data sink. + */ + @Benchmark + public void newPhilox4x64(Blackhole bh) { + for (int i = 0; i < SEEDS; i++) { + bh.consume(new Philox4x64(LONG_ARRAY_SEEDS[i])); + } + } + /** * Create a new instance using reflection with a cached constructor. * diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java index 2acd77f13..0cb2cc4ff 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java @@ -661,7 +661,26 @@ public enum RandomSource { * * @since 1.5 */ - L32_X64_MIX(ProviderBuilder.RandomSourceInternal.L32_X64_MIX); + L32_X64_MIX(ProviderBuilder.RandomSourceInternal.L32_X64_MIX), + /** + * Source of randomness is {@link org.apache.commons.rng.core.source32.Philox4x32}. + *

+ * @since 1.7 + */ + PHILOX_4X32(ProviderBuilder.RandomSourceInternal.PHILOX_4X32), + /** + * Source of randomness is {@link org.apache.commons.rng.core.source64.Philox4x64}. + * + * @since 1.7 + */ + PHILOX_4X64(ProviderBuilder.RandomSourceInternal.PHILOX_4X64); + /** Internal identifier. */ private final ProviderBuilder.RandomSourceInternal internalIdentifier; diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java index def9a36b4..a18450616 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java @@ -47,6 +47,7 @@ import org.apache.commons.rng.core.source32.DotyHumphreySmallFastCounting32; import org.apache.commons.rng.core.source32.JenkinsSmallFast32; import org.apache.commons.rng.core.source32.L32X64Mix; +import org.apache.commons.rng.core.source32.Philox4x32; import org.apache.commons.rng.core.source64.SplitMix64; import org.apache.commons.rng.core.source64.XorShift1024Star; import org.apache.commons.rng.core.source64.XorShift1024StarPhi; @@ -74,6 +75,7 @@ import org.apache.commons.rng.core.source64.L128X1024Mix; import org.apache.commons.rng.core.source64.L128X128Mix; import org.apache.commons.rng.core.source64.L128X256Mix; +import org.apache.commons.rng.core.source64.Philox4x64; /** * RNG builder. @@ -270,6 +272,7 @@ public enum RandomSourceInternal { PCG_MCG_XSH_RS_32(PcgMcgXshRs32.class, 1, NativeSeedType.LONG), + /** Source of randomness is {@link MiddleSquareWeylSequence}. */ MSWS(MiddleSquareWeylSequence.class, // Many partially zero seeds can create low quality initial output. @@ -439,8 +442,13 @@ private long[] createMswsSeed(UniformRandomProvider source) { /** Source of randomness is {@link L32X64Mix}. */ L32_X64_MIX(L32X64Mix.class, 4, 2, 4, - NativeSeedType.INT_ARRAY); - + NativeSeedType.INT_ARRAY), + /** Source of randomness is {@link Philox4x32}. */ + PHILOX_4X32(Philox4x32.class, + 6, 0, 2, NativeSeedType.INT_ARRAY), + /** Source of randomness is {@link Philox4x64}. */ + PHILOX_4X64(Philox4x64 .class, + 6, 0, 2, NativeSeedType.LONG_ARRAY); /** Source type. */ private final Class rng; /** Native seed size. Used for array seeds. */ diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java index f4f5957e4..ec312340c 100644 --- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java +++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java @@ -106,6 +106,8 @@ class RandomSourceInternalParametricTest { EXPECTED_SEED_BYTES.put(RandomSourceInternal.L128_X256_MIX, longBytes * 8); EXPECTED_SEED_BYTES.put(RandomSourceInternal.L128_X1024_MIX, longBytes * 20); EXPECTED_SEED_BYTES.put(RandomSourceInternal.L32_X64_MIX, intBytes * 4); + EXPECTED_SEED_BYTES.put(RandomSourceInternal.PHILOX_4X32, intBytes * 6); + EXPECTED_SEED_BYTES.put(RandomSourceInternal.PHILOX_4X64, longBytes * 6); // ... add more here. // Verify the seed byte size is reflected in the enum javadoc for RandomSource. }