ContinuousUniformSampler.java

  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. import org.apache.commons.rng.UniformRandomProvider;

  19. /**
  20.  * Sampling from a uniform distribution.
  21.  *
  22.  * <p>Sampling uses {@link UniformRandomProvider#nextDouble()}.</p>
  23.  *
  24.  * @since 1.0
  25.  */
  26. public class ContinuousUniformSampler
  27.     extends SamplerBase
  28.     implements SharedStateContinuousSampler {
  29.     /** The minimum ULP gap for the open interval when the doubles have the same sign. */
  30.     private static final int MIN_ULP_SAME_SIGN = 2;
  31.     /** The minimum ULP gap for the open interval when the doubles have the opposite sign. */
  32.     private static final int MIN_ULP_OPPOSITE_SIGN = 3;

  33.     /** Lower bound. */
  34.     private final double lo;
  35.     /** Higher bound. */
  36.     private final double hi;
  37.     /** Underlying source of randomness. */
  38.     private final UniformRandomProvider rng;

  39.     /**
  40.      * Specialization to sample from an open interval {@code (lo, hi)}.
  41.      */
  42.     private static final class OpenIntervalContinuousUniformSampler extends ContinuousUniformSampler {
  43.         /**
  44.          * @param rng Generator of uniformly distributed random numbers.
  45.          * @param lo Lower bound.
  46.          * @param hi Higher bound.
  47.          */
  48.         OpenIntervalContinuousUniformSampler(UniformRandomProvider rng, double lo, double hi) {
  49.             super(rng, lo, hi);
  50.         }

  51.         @Override
  52.         public double sample() {
  53.             final double x = super.sample();
  54.             // Due to rounding using a variate u in the open interval (0,1) with the original
  55.             // algorithm may generate a value at the bound. Thus the bound is explicitly tested
  56.             // and the sample repeated if necessary.
  57.             if (x == getHi() || x == getLo()) {
  58.                 return sample();
  59.             }
  60.             return x;
  61.         }

  62.         @Override
  63.         public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
  64.             return new OpenIntervalContinuousUniformSampler(rng, getLo(), getHi());
  65.         }
  66.     }

  67.     /**
  68.      * Create an instance.
  69.      *
  70.      * @param rng Generator of uniformly distributed random numbers.
  71.      * @param lo Lower bound.
  72.      * @param hi Higher bound.
  73.      */
  74.     public ContinuousUniformSampler(UniformRandomProvider rng,
  75.                                     double lo,
  76.                                     double hi) {
  77.         super(null);
  78.         this.rng = rng;
  79.         this.lo = lo;
  80.         this.hi = hi;
  81.     }

  82.     /** {@inheritDoc} */
  83.     @Override
  84.     public double sample() {
  85.         final double u = rng.nextDouble();
  86.         return u * hi + (1 - u) * lo;
  87.     }

  88.     /**
  89.      * Gets the lower bound. This is deliberately scoped as package private.
  90.      *
  91.      * @return the lower bound
  92.      */
  93.     double getLo() {
  94.         return lo;
  95.     }

  96.     /**
  97.      * Gets the higher bound. This is deliberately scoped as package private.
  98.      *
  99.      * @return the higher bound
  100.      */
  101.     double getHi() {
  102.         return hi;
  103.     }

  104.     /** {@inheritDoc} */
  105.     @Override
  106.     public String toString() {
  107.         return "Uniform deviate [" + rng.toString() + "]";
  108.     }

  109.     /**
  110.      * {@inheritDoc}
  111.      *
  112.      * @since 1.3
  113.      */
  114.     @Override
  115.     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
  116.         return new ContinuousUniformSampler(rng, lo, hi);
  117.     }

  118.     /**
  119.      * Creates a new continuous uniform distribution sampler.
  120.      *
  121.      * @param rng Generator of uniformly distributed random numbers.
  122.      * @param lo Lower bound.
  123.      * @param hi Higher bound.
  124.      * @return the sampler
  125.      * @since 1.3
  126.      */
  127.     public static SharedStateContinuousSampler of(UniformRandomProvider rng,
  128.                                                   double lo,
  129.                                                   double hi) {
  130.         return new ContinuousUniformSampler(rng, lo, hi);
  131.     }

  132.     /**
  133.      * Creates a new continuous uniform distribution sampler.
  134.      *
  135.      * <p>The bounds can be optionally excluded to sample from the open interval
  136.      * {@code (lower, upper)}. In this case if the bounds have the same sign the
  137.      * open interval must contain at least 1 double value between the limits; if the
  138.      * bounds have opposite signs the open interval must contain at least 2 double values
  139.      * between the limits excluding {@code -0.0}. Thus the interval {@code (-x,x)} will
  140.      * raise an exception when {@code x} is {@link Double#MIN_VALUE}.
  141.      *
  142.      * @param rng Generator of uniformly distributed random numbers.
  143.      * @param lo Lower bound.
  144.      * @param hi Higher bound.
  145.      * @param excludeBounds Set to {@code true} to use the open interval
  146.      * {@code (lower, upper)}.
  147.      * @return the sampler
  148.      * @throws IllegalArgumentException If the open interval is invalid.
  149.      * @since 1.4
  150.      */
  151.     public static SharedStateContinuousSampler of(UniformRandomProvider rng,
  152.                                                   double lo,
  153.                                                   double hi,
  154.                                                   boolean excludeBounds) {
  155.         if (excludeBounds) {
  156.             if (!validateOpenInterval(lo, hi)) {
  157.                 throw new IllegalArgumentException("Invalid open interval (" +
  158.                                                     lo + "," + hi + ")");
  159.             }
  160.             return new OpenIntervalContinuousUniformSampler(rng, lo, hi);
  161.         }
  162.         return new ContinuousUniformSampler(rng, lo, hi);
  163.     }

  164.     /**
  165.      * Check that the open interval is valid. It must contain at least one double value
  166.      * between the limits if the signs are the same, or two double values between the limits
  167.      * if the signs are different (excluding {@code -0.0}).
  168.      *
  169.      * @param lo Lower bound.
  170.      * @param hi Higher bound.
  171.      * @return false is the interval is invalid
  172.      */
  173.     private static boolean validateOpenInterval(double lo, double hi) {
  174.         // Get the raw bit representation.
  175.         long bitsx = Double.doubleToRawLongBits(lo);
  176.         long bitsy = Double.doubleToRawLongBits(hi);
  177.         // xor will set the sign bit if the signs are different
  178.         if ((bitsx ^ bitsy) < 0) {
  179.             // Opposite signs. Drop the sign bit to represent the count of
  180.             // bits above +0.0. When combined this should be above 2.
  181.             // This prevents the range (-Double.MIN_VALUE, Double.MIN_VALUE)
  182.             // which cannot be sampled unless the uniform deviate u=0.5.
  183.             // (MAX_VALUE has all bits set except the most significant sign bit.)
  184.             bitsx &= Long.MAX_VALUE;
  185.             bitsy &= Long.MAX_VALUE;
  186.             return !lessThanUnsigned(bitsx + bitsy, MIN_ULP_OPPOSITE_SIGN);
  187.         }
  188.         // Same signs, subtraction will count the ULP difference.
  189.         // This should be above 1.
  190.         return Math.abs(bitsx - bitsy) >= MIN_ULP_SAME_SIGN;
  191.     }

  192.     /**
  193.      * Compares two {@code long} values numerically treating the values as unsigned
  194.      * to test if the first value is less than the second value.
  195.      *
  196.      * <p>See Long.compareUnsigned(long, long) in JDK 1.8.
  197.      *
  198.      * @param x the first value
  199.      * @param y the second value
  200.      * @return true if {@code x < y}
  201.      */
  202.     private static boolean lessThanUnsigned(long x, long y) {
  203.         return x + Long.MIN_VALUE < y + Long.MIN_VALUE;
  204.     }
  205. }