001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.rng; 018 019import java.util.stream.Stream; 020 021/** 022 * Applies to generators that can be advanced an arbitrary number of steps of the output 023 * sequence in a single operation. 024 * 025 * <p>Implementations must ensure that a jump of a specified {@code distance} will advance 026 * the state cycle sufficiently that an equivalent number of sequential calls to the 027 * original provider will <strong>not overlap</strong> output from the advanced 028 * provider.</p> 029 * 030 * <p>For many applications, it suffices to jump forward by a power of two or some small 031 * multiple of a power of two, but this power of two may not be representable as a 032 * {@code long} value. To avoid the use of {@link java.math.BigInteger BigInteger} values 033 * as jump distances, double values are used instead.</p> 034 * 035 * <p>Typical usage in a multithreaded application is to create a single 036 * {@link ArbitrarilyJumpableUniformRandomProvider} and {@link #jump(double) jump} the 037 * generator forward while passing each copy generator to a worker thread. The jump 038 * {@code distance} should be sufficient to cover all expected output by each worker. 039 * Since each copy generator is also an {@link ArbitrarilyJumpableUniformRandomProvider} 040 * with care it is possible to further distribute generators within the original jump 041 * {@code distance} and use the entire state cycle in different ways.</p> 042 * 043 * @since 1.7 044 */ 045public interface ArbitrarilyJumpableUniformRandomProvider extends UniformRandomProvider { 046 /** 047 * Creates a copy of the {@link ArbitrarilyJumpableUniformRandomProvider} and then advances 048 * the state cycle of the current instance by the specified {@code distance}. 049 * The copy is returned. 050 * 051 * <p>The current state will be advanced in a single operation by the equivalent of a 052 * number of sequential calls to a method that updates the state cycle of the provider.</p> 053 * 054 * <p>Repeat invocations of this method will create a series of generators 055 * that are uniformly spaced at intervals of the output sequence. Each generator provides 056 * non-overlapping output for the length specified by {@code distance} for use in parallel 057 * computations.</p> 058 * 059 * @param distance Distance to jump forward with the state cycle. 060 * @return A copy of the current state. 061 * @throws IllegalArgumentException if {@code distance} is negative, 062 * or is greater than the period of this generator. 063 */ 064 ArbitrarilyJumpableUniformRandomProvider jump(double distance); 065 066 /** 067 * Creates a copy of the {@link ArbitrarilyJumpableUniformRandomProvider} and then advances 068 * the state cycle of the current instance by a distance equal to 2<sup>{@code logDistance}</sup>. 069 * The copy is returned. 070 * 071 * <p>The current state will be advanced in a single operation by the equivalent of a 072 * number of sequential calls to a method that updates the state cycle of the provider.</p> 073 * 074 * <p>Repeat invocations of this method will create a series of generators 075 * that are uniformly spaced at intervals of the output sequence. Each generator provides 076 * non-overlapping output for the length specified by 2<sup>{@code logDistance}</sup> for use 077 * in parallel computations.</p> 078 * 079 * @param logDistance Base-2 logarithm of the distance to jump forward with the state cycle. 080 * @return A copy of the current state. 081 * @throws IllegalArgumentException if 2<sup>{@code logDistance}</sup> 082 * is greater than the period of this generator. 083 */ 084 ArbitrarilyJumpableUniformRandomProvider jumpPowerOfTwo(int logDistance); 085 086 /** 087 * Returns an effectively unlimited stream of new random generators, each of which 088 * implements the {@link ArbitrarilyJumpableUniformRandomProvider} interface. The 089 * generators are output at integer multiples of the specified jump {@code distance} 090 * in the generator's state cycle. 091 * 092 * @param distance Distance to jump forward with the state cycle. 093 * @return a stream of random generators. 094 * @throws IllegalArgumentException if {@code distance} is negative, 095 * or is greater than the period of this generator. 096 */ 097 default Stream<ArbitrarilyJumpableUniformRandomProvider> jumps(double distance) { 098 UniformRandomProviderSupport.validateJumpDistance(distance); 099 return Stream.generate(() -> jump(distance)).sequential(); 100 } 101 102 /** 103 * Returns a stream producing the given {@code streamSize} number of new random 104 * generators, each of which implements the {@link ArbitrarilyJumpableUniformRandomProvider} 105 * interface. The generators are output at integer multiples of the specified jump 106 * {@code distance} in the generator's state cycle. 107 * 108 * @param streamSize Number of objects to generate. 109 * @param distance Distance to jump forward with the state cycle. 110 * @return a stream of random generators; the stream is limited to the given 111 * {@code streamSize}. 112 * @throws IllegalArgumentException if {@code streamSize} is negative; 113 * or if {@code distance} is negative, or is greater than the period of this generator. 114 */ 115 default Stream<ArbitrarilyJumpableUniformRandomProvider> jumps(long streamSize, 116 double distance) { 117 UniformRandomProviderSupport.validateStreamSize(streamSize); 118 return jumps(distance).limit(streamSize); 119 } 120}