View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.rng.sampling.distribution;
18  
19  import org.apache.commons.rng.UniformRandomProvider;
20  import org.apache.commons.rng.sampling.RandomAssert;
21  import org.apache.commons.rng.simple.RandomSource;
22  import org.junit.Assert;
23  import org.junit.Test;
24  
25  /**
26   * Test for the {@link GeometricSampler}. The tests hit edge cases for the sampler.
27   */
28  public class GeometricSamplerTest {
29      /**
30       * Test the edge case where the probability of success is 1. This is a valid geometric
31       * distribution where the sample should always be 0.
32       */
33      @Test
34      public void testProbabilityOfSuccessIsOneGeneratesZeroForSamples() {
35          final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
36          final SharedStateDiscreteSampler sampler = GeometricSampler.of(rng, 1);
37          // All samples should be 0
38          for (int i = 0; i < 10; i++) {
39              Assert.assertEquals("p=1 should have 0 for all samples", 0, sampler.sample());
40          }
41      }
42  
43      /**
44       * Test to demonstrate that any probability of success under one produces a valid
45       * mean for the exponential distribution.
46       */
47      @Test
48      public void testProbabilityOfSuccessUnderOneIsValid() {
49          // The sampler explicitly handles probabilityOfSuccess == 1 as an edge case.
50          // Anything under it should be valid for sampling from an ExponentialDistribution.
51          final double probabilityOfSuccess = Math.nextAfter(1, -1);
52          // Map to the mean
53          final double exponentialMean = 1.0 / (-Math.log1p(-probabilityOfSuccess));
54          // As long as this is finite positive then the sampler is valid
55          Assert.assertTrue(exponentialMean > 0 && exponentialMean <= Double.MAX_VALUE);
56          // The internal exponential sampler validates the mean so demonstrate creating a
57          // geometric sampler does not throw.
58          final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
59          GeometricSampler.of(rng, probabilityOfSuccess);
60      }
61  
62      /**
63       * Test the edge case where the probability of success is 1 since it uses a different
64       * {@link Object#toString()} method to the normal case tested elsewhere.
65       */
66      @Test
67      public void testProbabilityOfSuccessIsOneSamplerToString() {
68          final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
69          final SharedStateDiscreteSampler sampler = GeometricSampler.of(unusedRng, 1);
70          Assert.assertTrue("Missing 'Geometric' from toString",
71              sampler.toString().contains("Geometric"));
72      }
73  
74      /**
75       * Test the edge case where the probability of success is nearly 0. This is a valid geometric
76       * distribution but the sample is clipped to max integer value because the underlying
77       * exponential has a mean of positive infinity (effectively the sample is from a truncated
78       * distribution).
79       *
80       * <p>This test can be changed in future if a lower bound limit for the probability of success
81       * is introduced.
82       */
83      @Test
84      public void testProbabilityOfSuccessIsAlmostZeroGeneratesMaxValueForSamples() {
85          final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
86          final SharedStateDiscreteSampler sampler = GeometricSampler.of(rng, Double.MIN_VALUE);
87          // All samples should be max value
88          for (int i = 0; i < 10; i++) {
89              Assert.assertEquals("p=(almost 0) should have Integer.MAX_VALUE for all samples",
90                  Integer.MAX_VALUE, sampler.sample());
91          }
92      }
93  
94      /**
95       * Test probability of success {@code >1} is not allowed.
96       */
97      @Test(expected = IllegalArgumentException.class)
98      public void testProbabilityOfSuccessAboveOneThrows() {
99          final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
100         final double probabilityOfSuccess = Math.nextUp(1.0);
101         GeometricSampler.of(unusedRng, probabilityOfSuccess);
102     }
103 
104     /**
105      * Test probability of success {@code 0} is not allowed.
106      */
107     @Test(expected = IllegalArgumentException.class)
108     public void testProbabilityOfSuccessIsZeroThrows() {
109         final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
110         final double probabilityOfSuccess = 0;
111         GeometricSampler.of(unusedRng, probabilityOfSuccess);
112     }
113 
114     /**
115      * Test the SharedStateSampler implementation.
116      */
117     @Test
118     public void testSharedStateSampler() {
119         testSharedStateSampler(0.5);
120     }
121 
122     /**
123      * Test the SharedStateSampler implementation with the edge case when the probability of
124      * success is {@code 1.0}.
125      */
126     @Test
127     public void testSharedStateSamplerWithProbabilityOfSuccessOne() {
128         testSharedStateSampler(1.0);
129     }
130 
131     /**
132      * Test the SharedStateSampler implementation.
133      *
134      * @param probabilityOfSuccess Probability of success.
135      */
136     private static void testSharedStateSampler(double probabilityOfSuccess) {
137         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
138         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
139         final SharedStateDiscreteSampler sampler1 =
140             GeometricSampler.of(rng1, probabilityOfSuccess);
141         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
142         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
143     }
144 }