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.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.Test;
23  
24  /**
25   * Test for the {@link GeometricSampler}. The tests hit edge cases for the sampler.
26   */
27  class GeometricSamplerTest {
28      /**
29       * Test the edge case where the probability of success is 1. This is a valid geometric
30       * distribution where the sample should always be 0.
31       */
32      @Test
33      void testProbabilityOfSuccessIsOneGeneratesZeroForSamples() {
34          final UniformRandomProvider rng = RandomAssert.seededRNG();
35          final SharedStateDiscreteSampler sampler = GeometricSampler.of(rng, 1);
36          // All samples should be 0
37          for (int i = 0; i < 10; i++) {
38              Assertions.assertEquals(0, sampler.sample(), "p=1 should have 0 for all samples");
39          }
40      }
41  
42      /**
43       * Test to demonstrate that any probability of success under one produces a valid
44       * mean for the exponential distribution.
45       */
46      @Test
47      void testProbabilityOfSuccessUnderOneIsValid() {
48          // The sampler explicitly handles probabilityOfSuccess == 1 as an edge case.
49          // Anything under it should be valid for sampling from an ExponentialDistribution.
50          final double probabilityOfSuccess = Math.nextDown(1);
51          // Map to the mean
52          final double exponentialMean = 1.0 / (-Math.log1p(-probabilityOfSuccess));
53          // As long as this is finite positive then the sampler is valid
54          Assertions.assertTrue(exponentialMean > 0 && exponentialMean <= Double.MAX_VALUE);
55          // The internal exponential sampler validates the mean so demonstrate creating a
56          // geometric sampler does not throw.
57          final UniformRandomProvider rng = RandomAssert.seededRNG();
58          GeometricSampler.of(rng, probabilityOfSuccess);
59      }
60  
61      /**
62       * Test the edge case where the probability of success is 1 since it uses a different
63       * {@link Object#toString()} method to the normal case tested elsewhere.
64       */
65      @Test
66      void testProbabilityOfSuccessIsOneSamplerToString() {
67          final UniformRandomProvider unusedRng = RandomAssert.seededRNG();
68          final SharedStateDiscreteSampler sampler = GeometricSampler.of(unusedRng, 1);
69          Assertions.assertTrue(sampler.toString().contains("Geometric"),
70              "Missing 'Geometric' from toString");
71      }
72  
73      /**
74       * Test the edge case where the probability of success is nearly 0. This is a valid geometric
75       * distribution but the sample is clipped to max integer value because the underlying
76       * exponential has a mean of positive infinity (effectively the sample is from a truncated
77       * distribution).
78       *
79       * <p>This test can be changed in future if a lower bound limit for the probability of success
80       * is introduced.
81       */
82      @Test
83      void testProbabilityOfSuccessIsAlmostZeroGeneratesMaxValueForSamples() {
84          final UniformRandomProvider rng = RandomAssert.seededRNG();
85          final SharedStateDiscreteSampler sampler = GeometricSampler.of(rng, Double.MIN_VALUE);
86          // All samples should be max value
87          for (int i = 0; i < 10; i++) {
88              Assertions.assertEquals(Integer.MAX_VALUE, sampler.sample(),
89                  "p=(almost 0) should have Integer.MAX_VALUE for all samples");
90          }
91      }
92  
93      /**
94       * Test probability of success {@code >1} is not allowed.
95       */
96      @Test
97      void testProbabilityOfSuccessAboveOneThrows() {
98          final UniformRandomProvider unusedRng = RandomAssert.seededRNG();
99          final double probabilityOfSuccess = Math.nextUp(1.0);
100         Assertions.assertThrows(IllegalArgumentException.class,
101             () -> GeometricSampler.of(unusedRng, probabilityOfSuccess));
102     }
103 
104     /**
105      * Test probability of success {@code 0} is not allowed.
106      */
107     @Test
108     void testProbabilityOfSuccessIsZeroThrows() {
109         final UniformRandomProvider unusedRng = RandomAssert.seededRNG();
110         final double probabilityOfSuccess = 0;
111         Assertions.assertThrows(IllegalArgumentException.class,
112             () -> GeometricSampler.of(unusedRng, probabilityOfSuccess));
113     }
114 
115     /**
116      * Test the SharedStateSampler implementation.
117      */
118     @Test
119     void testSharedStateSampler() {
120         testSharedStateSampler(0.5);
121     }
122 
123     /**
124      * Test the SharedStateSampler implementation with the edge case when the probability of
125      * success is {@code 1.0}.
126      */
127     @Test
128     void testSharedStateSamplerWithProbabilityOfSuccessOne() {
129         testSharedStateSampler(1.0);
130     }
131 
132     /**
133      * Test the SharedStateSampler implementation.
134      *
135      * @param probabilityOfSuccess Probability of success.
136      */
137     private static void testSharedStateSampler(double probabilityOfSuccess) {
138         final UniformRandomProvider rng1 = RandomAssert.seededRNG();
139         final UniformRandomProvider rng2 = RandomAssert.seededRNG();
140         final SharedStateDiscreteSampler sampler1 =
141             GeometricSampler.of(rng1, probabilityOfSuccess);
142         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
143         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
144     }
145 }