This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git

commit 6299f15f9ecb1235089b15193321f23250b7f44e
Author: Alex Herbert <[email protected]>
AuthorDate: Wed Feb 18 16:30:39 2026 +0000

    RNG-189: Add arbitrary jump examples to the user guide
---
 .../ArbitrarilyJumpableUniformRandomProvider.java  |  2 +-
 .../commons/rng/core/source64/Philox4x64Test.java  | 40 ++++++++++++
 src/site/apt/userguide/rng.apt                     | 72 +++++++++++++++++++++-
 3 files changed, 110 insertions(+), 4 deletions(-)

diff --git 
a/commons-rng-client-api/src/main/java/org/apache/commons/rng/ArbitrarilyJumpableUniformRandomProvider.java
 
b/commons-rng-client-api/src/main/java/org/apache/commons/rng/ArbitrarilyJumpableUniformRandomProvider.java
index 2470a4ce..4414bff3 100644
--- 
a/commons-rng-client-api/src/main/java/org/apache/commons/rng/ArbitrarilyJumpableUniformRandomProvider.java
+++ 
b/commons-rng-client-api/src/main/java/org/apache/commons/rng/ArbitrarilyJumpableUniformRandomProvider.java
@@ -36,7 +36,7 @@ import java.util.stream.Stream;
  * {@link ArbitrarilyJumpableUniformRandomProvider} and {@link #jump(double) 
jump} the
  * generator forward while passing each copy generator to a worker thread. The 
jump
  * {@code distance} should be sufficient to cover all expected output by each 
worker.
- * Since each copy generator is also a {@link 
ArbitrarilyJumpableUniformRandomProvider}
+ * Since each copy generator is also an {@link 
ArbitrarilyJumpableUniformRandomProvider}
  * with care it is possible to further distribute generators within the 
original jump
  * {@code distance} and use the entire state cycle in different ways.</p>
  *
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
index b8aac746..b160b92f 100644
--- 
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
@@ -22,6 +22,8 @@ import java.util.Arrays;
 import java.util.SplittableRandom;
 import java.util.stream.Stream;
 import java.util.stream.Stream.Builder;
+import org.apache.commons.rng.ArbitrarilyJumpableUniformRandomProvider;
+import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.core.RandomAssert;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -614,4 +616,42 @@ public class Philox4x64Test {
         // Return result with the same counter size
         return Arrays.copyOf(value, counter.length);
     }
+
+    @Test
+    void userGuideExample1() {
+        ArbitrarilyJumpableUniformRandomProvider jumpable = new 
Philox4x64(SEEDS[3]);
+
+        double distance = 42;
+        for (int i = 0; i < 5; i++) {
+            // Copy the state and then jump ahead
+            UniformRandomProvider copy = jumpable.jump(distance);
+
+            // Catch up the jump using the native 64-bit output
+            for (int j = 0; j < distance; j++) {
+                copy.nextLong();
+            }
+
+            // The copy matches the jumped generator
+            Assertions.assertEquals(copy.nextLong(), jumpable.nextLong());
+        }
+    }
+
+    @Test
+    void userGuideExample2() {
+        ArbitrarilyJumpableUniformRandomProvider jumpable = new 
Philox4x64(SEEDS[3]);
+
+        int logDistance = 123;
+        ArbitrarilyJumpableUniformRandomProvider copy = 
jumpable.jumpPowerOfTwo(logDistance);
+
+        // Catch up the jump using: 4 * 2^119 + 2^121 + 2^122
+        copy.jumpPowerOfTwo(logDistance - 4);
+        copy.jumpPowerOfTwo(logDistance - 4);
+        copy.jumpPowerOfTwo(logDistance - 2);
+        copy.jumpPowerOfTwo(logDistance - 4);
+        copy.jumpPowerOfTwo(logDistance - 1);
+        copy.jumpPowerOfTwo(logDistance - 4);
+
+        // The copy matches the jumped generator
+        Assertions.assertEquals(copy.nextLong(), jumpable.nextLong());
+    }
 }
diff --git a/src/site/apt/userguide/rng.apt b/src/site/apt/userguide/rng.apt
index 32346ad0..86fb3391 100644
--- a/src/site/apt/userguide/rng.apt
+++ b/src/site/apt/userguide/rng.apt
@@ -397,9 +397,56 @@ jumpable.jumps(streamSize).forEach(rng -> {
     Note that here the stream of RNGs is sequential; each RNG is used within a 
potentially
     long-running task that can run concurrently with other tasks using an 
executor service.
 
-    In the above example, the source is known to implement the 
<<<JumpableUniformRandomProvider>>> interface.
+  * The <<<ArbitrarilyJumpableUniformRandomProvider>>> interface allows 
creation of a copy of the generator and
+    advances the state of the current generator an <arbitrary> number of steps 
in a single jump.
+    Jump distances are supported using a <<<double>>> or using a power-of-2. 
Streams of jumpable
+    generators can be created using a <<<double>>> distance.
+    Since each copy generator is also an 
<<<ArbitrarilyJumpableUniformRandomProvider>>>
+    with care it is possible to further distribute generators within the 
original jump
+    distance and use the entire state cycle in different ways.
+
++--------------------------+
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.ArbitrarilyJumpableUniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+
+RandomSource source = RandomSource.PHILOX_4X64; // Known to be arbitrarily 
jumpable.
+
+ArbitrarilyJumpableUniformRandomProvider jumpable = 
(ArbitrarilyJumpableUniformRandomProvider) source.create();
+
+double distance = 42;
+for (int i = 0; i < 5; i++) {
+    // Copy the state and then jump ahead
+    UniformRandomProvider copy = jumpable.jump(distance);
+
+    // Catch up the jump using the native 64-bit output
+    for (int j = 0; j < distance; j++) {
+        copy.nextLong();
+    }
+
+    // The copy matches the jumped generator
+    assert copy.nextLong() == jumpable.nextLong();
+}
+
+int logDistance = 123;
+ArbitrarilyJumpableUniformRandomProvider copy = 
jumpable.jumpPowerOfTwo(logDistance);
+
+// Catch up the jump using: 4 * 2^119 + 2^121 + 2^122
+copy.jumpPowerOfTwo(logDistance - 4);
+copy.jumpPowerOfTwo(logDistance - 4);
+copy.jumpPowerOfTwo(logDistance - 2);
+copy.jumpPowerOfTwo(logDistance - 4);
+copy.jumpPowerOfTwo(logDistance - 1);
+copy.jumpPowerOfTwo(logDistance - 4);
+
+// The copy matches the jumped generator
+assert copy.nextLong() == jumpable.nextLong();
++--------------------------+
+
+    In the above examples, the source is known to implement the appropriate 
jumpable interface.
     Not all generators support this functionality. You can determine if a 
<<<RandomSource>>> is
-    jumpable without creating one using the instance methods 
<<<isJumpable()>>> and <<<isLongJumpable()>>>.
+    jumpable without creating one using the instance methods 
<<<isJumpable()>>>,
+    <<<isLongJumpable()>>> and <<<isArbitrarilyJumpable>>>.
 
 +--------------------------+
 import org.apache.commons.rng.simple.RandomSource;
@@ -412,6 +459,24 @@ public void initialise(RandomSource source) {
 }
 +--------------------------+
 
+    Jumping can be used to create a series of non-overlapping generators
+    for use in multithreaded applications.
+    Note that there is not a one-to-one relationship between the number of 
output random
+    values from a provider and the number of steps from the underlying state 
cycle. This
+    is due to:
+
+        * Possible use of rejection algorithms to output a random value using 
multiple
+          values from the state cycle.
+
+        * The number of bits required to generate a random value differing 
from the
+          number of bits generated by the underlying source of randomness. For 
example
+          generation of a 64-bit <<<long>>> value using a 32-bit source of 
randomness.
+
+    Users are advised to use jumping generators with care to avoid
+    overlapping output of multiple generators in parallel computations.
+    A cautious approach is to use a jump distance far larger than the expected
+    output length used by each generator.
+
   * The <<<SplittableUniformRandomProvider>>> interface allows splitting a 
generator into two
     objects (the original and a new instance) each of which implements the 
same interface (and
     can be recursively split indefinitely). This can be used for parallel 
computations where the
@@ -650,7 +715,8 @@ double[] coordinate = sampler.sample();
   * Interfaces <<<RestorableUniformRandomProvider>>> and 
<<<RandomProviderState>>>
     provide the "save/restore" API.
 
-  * Interfaces <<<JumpableUniformRandomProvider>>> and 
<<<LongJumpableUniformRandomProvider>>>
+  * Interfaces <<<JumpableUniformRandomProvider>>>, 
<<<LongJumpableUniformRandomProvider>>>
+    and <<<ArbitrarilyJumpableUniformRandomProvider>>>
     provide the "copy and jump" API for parallel computations. These are 
suitable for tasks
     where the number of instances to use in parallel is known.
 

Reply via email to