StableSampler.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.  * Samples from a stable distribution.
  21.  *
  22.  * <p>Several different parameterizations exist for the stable distribution.
  23.  * This sampler uses the 0-parameterization distribution described in Nolan (2020) "Univariate Stable
  24.  * Distributions: Models for Heavy Tailed Data". Springer Series in Operations Research and
  25.  * Financial Engineering. Springer. Sections 1.7 and 3.3.3.
  26.  *
  27.  * <p>The random variable \( X \) has
  28.  * the stable distribution \( S(\alpha, \beta, \gamma, \delta; 0) \) if its characteristic
  29.  * function is given by:
  30.  *
  31.  * <p>\[ E(e^{iuX}) = \begin{cases} \exp \left (- \gamma^\alpha |u|^\alpha \left [1 - i \beta (\tan \frac{\pi \alpha}{2})(\text{sgn}(u)) \right ] + i \delta u \right ) &amp; \alpha \neq 1 \\
  32.  * \exp \left (- \gamma |u| \left [1 + i \beta \frac{2}{\pi} (\text{sgn}(u)) \log |u| \right ] + i \delta u \right ) &amp; \alpha = 1 \end{cases} \]
  33.  *
  34.  * <p>The function is continuous with respect to all the parameters; the parameters \( \alpha \)
  35.  * and \( \beta \) determine the shape and the parameters \( \gamma \) and \( \delta \) determine
  36.  * the scale and location. The support of the distribution is:
  37.  *
  38.  * <p>\[ \text{support} f(x|\alpha,\beta,\gamma,\delta; 0) = \begin{cases} [\delta - \gamma \tan \frac{\pi \alpha}{2}, \infty) &amp; \alpha \lt 1\ and\ \beta = 1 \\
  39.  * (-\infty, \delta + \gamma \tan \frac{\pi \alpha}{2}] &amp; \alpha \lt 1\ and\ \beta = -1 \\
  40.  * (-\infty, \infty) &amp; otherwise \end{cases} \]
  41.  *
  42.  * <p>The implementation uses the Chambers-Mallows-Stuck (CMS) method as described in:
  43.  * <ul>
  44.  *  <li>Chambers, Mallows &amp; Stuck (1976) "A Method for Simulating Stable Random Variables".
  45.  *      Journal of the American Statistical Association. 71 (354): 340–344.
  46.  *  <li>Weron (1996) "On the Chambers-Mallows-Stuck method for simulating skewed stable
  47.  *      random variables". Statistics &amp; Probability Letters. 28 (2): 165–171.
  48.  * </ul>
  49.  *
  50.  * @see <a href="https://en.wikipedia.org/wiki/Stable_distribution">Stable distribution (Wikipedia)</a>
  51.  * @see <a href="https://link.springer.com/book/10.1007/978-3-030-52915-4">Nolan (2020) Univariate Stable Distributions</a>
  52.  * @see <a href="https://doi.org/10.1080%2F01621459.1976.10480344">Chambers et al (1976) JOASA 71: 340-344</a>
  53.  * @see <a href="https://doi.org/10.1016%2F0167-7152%2895%2900113-1">Weron (1996).
  54.  * Statistics &amp; Probability Letters. 28 (2): 165–171.</a>
  55.  * @since 1.4
  56.  */
  57. public abstract class StableSampler implements SharedStateContinuousSampler {
  58.     /** pi / 2. */
  59.     private static final double PI_2 = Math.PI / 2;
  60.     /** The alpha value for the Gaussian case. */
  61.     private static final double ALPHA_GAUSSIAN = 2;
  62.     /** The alpha value for the Cauchy case. */
  63.     private static final double ALPHA_CAUCHY = 1;
  64.     /** The alpha value for the Levy case. */
  65.     private static final double ALPHA_LEVY = 0.5;
  66.     /** The alpha value for the {@code alpha -> 0} to switch to using the Weron formula.
  67.      * Note that small alpha requires robust correction of infinite samples. */
  68.     private static final double ALPHA_SMALL = 0.02;
  69.     /** The beta value for the Levy case. */
  70.     private static final double BETA_LEVY = 1.0;
  71.     /** The gamma value for the normalized case. */
  72.     private static final double GAMMA_1 = 1.0;
  73.     /** The delta value for the normalized case. */
  74.     private static final double DELTA_0 = 0.0;
  75.     /** The tau value for zero. When tau is zero, this is effectively {@code beta = 0}. */
  76.     private static final double TAU_ZERO = 0.0;
  77.     /**
  78.      * The lower support for the distribution.
  79.      * This is the lower bound of {@code (-inf, +inf)}
  80.      * If the sample is not within this bound ({@code lower < x}) then it is either
  81.      * infinite or NaN and the result should be checked.
  82.      */
  83.     private static final double LOWER = Double.NEGATIVE_INFINITY;
  84.     /**
  85.      * The upper support for the distribution.
  86.      * This is the upper bound of {@code (-inf, +inf)}.
  87.      * If the sample is not within this bound ({@code x < upper}) then it is either
  88.      * infinite or NaN and the result should be checked.
  89.      */
  90.     private static final double UPPER = Double.POSITIVE_INFINITY;

  91.     /** Underlying source of randomness. */
  92.     private final UniformRandomProvider rng;

  93.     // Implementation notes
  94.     //
  95.     // The Chambers-Mallows-Stuck (CMS) method uses a uniform deviate u in (0, 1) and an
  96.     // exponential deviate w to compute a stable deviate. Chambers et al (1976) published
  97.     // a formula for alpha = 1 and alpha != 1. The function is discontinuous at alpha = 1
  98.     // and to address this a trigonmoic rearrangement was provided using half angles that
  99.     // is continuous with respect to alpha. The original discontinuous formulas were proven
  100.     // in Weron (1996). The CMS rearrangement creates a deviate in the 0-parameterization
  101.     // defined by Nolan (2020); the original discontinuous functions create a deviate in the
  102.     // 1-parameterization defined by Nolan. A shift can be used to convert one parameterisation
  103.     // to the other. The shift is the magnitude of the zeta term from the 1-parameterisation.
  104.     // The following table shows how the zeta term -> inf when alpha -> 1 for
  105.     // different beta (hence the discontinuity in the function):
  106.     //
  107.     // Zeta
  108.     //             Beta
  109.     // Alpha       1.0         0.5         0.25        0.1         0.0
  110.     // 0.001       0.001571    0.0007854   0.0003927   0.0001571   0.0
  111.     // 0.01        0.01571     0.007855    0.003927    0.001571    0.0
  112.     // 0.05        0.07870     0.03935     0.01968     0.007870    0.0
  113.     // 0.01        0.01571     0.007855    0.003927    0.001571    0.0
  114.     // 0.1         0.1584      0.07919     0.03960     0.01584     0.0
  115.     // 0.5         1.000       0.5000      0.2500      0.1000      0.0
  116.     // 0.9         6.314       3.157       1.578       0.6314      0.0
  117.     // 0.95        12.71       6.353       3.177       1.271       0.0
  118.     // 0.99        63.66       31.83       15.91       6.366       0.0
  119.     // 0.995       127.3       63.66       31.83       12.73       0.0
  120.     // 0.999       636.6       318.3       159.2       63.66       0.0
  121.     // 0.9995      1273        636.6       318.3       127.3       0.0
  122.     // 0.9999      6366        3183        1592        636.6       0.0
  123.     // 1.0         1.633E+16   8.166E+15   4.083E+15   1.633E+15   0.0
  124.     //
  125.     // For numerical simulation the 0-parameterization is favoured as it is continuous
  126.     // with respect to all the parameters. When approaching alpha = 1 the large magnitude
  127.     // of the zeta term used to shift the 1-parameterization results in cancellation and the
  128.     // number of bits of the output sample is effected. This sampler uses the CMS method with
  129.     // the continuous function as the base for the implementation. However it is not suitable
  130.     // for all values of alpha and beta.
  131.     //
  132.     // The method computes a value log(z) with z in the interval (0, inf). When z is 0 or infinite
  133.     // the computation can return invalid results. The open bound for the deviate u avoids
  134.     // generating an extreme value that results in cancellation, z=0 and an invalid expression.
  135.     // However due to floating point error this can occur
  136.     // when u is close to 0 or 1, and beta is -1 or 1. Thus it is not enough to create
  137.     // u by avoiding 0 or 1 and further checks are required.
  138.     // The division by the deviate w also results in an invalid expression as the term z becomes
  139.     // infinite as w -> 0. It should be noted that such events are extremely rare
  140.     // (frequency in the 1 in 10^15), or will not occur at all depending on the parameters alpha
  141.     // and beta.
  142.     //
  143.     // When alpha -> 0 then the distribution is extremely long tailed and the expression
  144.     // using log(z) often computes infinity. Certain parameters can create NaN due to
  145.     // 0 / 0, 0 * inf, or inf - inf. Thus the implementation must check the final result
  146.     // and perform a correction if required, or generate another sample.
  147.     // Correcting the original CMS formula has many edge cases depending on parameters. The
  148.     // alternative formula provided by Weron is easier to correct when infinite values are
  149.     // created. This correction is made easier by knowing that u is not 0 or 1 as certain
  150.     // conditions on the intermediate terms can be eliminated. The implementation
  151.     // thus generates u in the open interval (0,1) but leaves w unchecked and potentially 0.
  152.     // The sample is generated and the result tested against the expected support. This detects
  153.     // any NaN and infinite values. Incorrect samples due to the inability to compute log(z)
  154.     // (extremely rare) and samples where alpha -> 0 has resulted in an infinite expression
  155.     // for the value d are corrected using the Weron formula and returned within the support.
  156.     //
  157.     // The CMS algorithm is continuous for the parameters. However when alpha=1 or beta=0
  158.     // many terms cancel and these cases are handled with specialised implementations.
  159.     // The beta=0 case implements the same CMS algorithm with certain terms eliminated.
  160.     // Correction uses the alternative Weron formula. When alpha=1 the CMS algorithm can
  161.     // be corrected from infinite cases due to assumptions on the intermediate terms.
  162.     //
  163.     // The following table show the failure frequency (result not finite or, when beta=+/-1,
  164.     // within the support) for the CMS algorithm computed using 2^30 random deviates.
  165.     //
  166.     // CMS failure rate
  167.     //             Beta
  168.     // Alpha       1.0         0.5         0.25        0.1         0.0
  169.     // 1.999       0.0         0.0         0.0         0.0         0.0
  170.     // 1.99        0.0         0.0         0.0         0.0         0.0
  171.     // 1.9         0.0         0.0         0.0         0.0         0.0
  172.     // 1.5         0.0         0.0         0.0         0.0         0.0
  173.     // 1.1         0.0         0.0         0.0         0.0         0.0
  174.     // 1.0         0.0         0.0         0.0         0.0         0.0
  175.     // 0.9         0.0         0.0         0.0         0.0         0.0
  176.     // 0.5         0.0         0.0         0.0         0.0         0.0
  177.     // 0.25        0.0         0.0         0.0         0.0         0.0
  178.     // 0.1         0.0         0.0         0.0         0.0         0.0
  179.     // 0.05        0.0003458   0.0         0.0         0.0         0.0
  180.     // 0.02        0.009028    6.938E-7    7.180E-7    7.320E-7    6.873E-7
  181.     // 0.01        0.004878    0.0008555   0.0008553   0.0008554   0.0008570
  182.     // 0.005       0.1519      0.02896     0.02897     0.02897     0.02897
  183.     // 0.001       0.6038      0.3903      0.3903      0.3903      0.3903
  184.     //
  185.     // The sampler switches to using the error checked Weron implementation when alpha < 0.02.
  186.     // Unit tests demonstrate the two samplers (CMS or Weron) product the same result within
  187.     // a tolerance. The switch point is based on a consistent failure rate above 1 in a million.
  188.     // At this point zeta is small and cancellation leading to loss of bits in the sample is
  189.     // minimal.
  190.     //
  191.     // In common use the sampler will not have a measurable failure rate. The output will
  192.     // be continuous as alpha -> 1 and beta -> 0. The evaluated function produces symmetric
  193.     // samples when u and beta are mirrored around 0.5 and 0 respectively. To achieve this
  194.     // the computation of certain parameters has been changed from the original implementation
  195.     // to avoid evaluating Math.tan outside the interval (-pi/2, pi/2).
  196.     //
  197.     // Note: Chambers et al (1976) use an approximation to tan(x) / x in the RSTAB routine.
  198.     // A JMH performance test is available in the RNG examples module comparing Math.tan
  199.     // with various approximations. The functions are faster than Math.tan(x) / x.
  200.     // This implementation uses a higher accuracy approximation than the original RSTAB
  201.     // implementation; it has a mean ULP difference to Math.tan of less than 1 and has
  202.     // a noticeable performance gain.

  203.     /**
  204.      * Base class for implementations of a stable distribution that requires an exponential
  205.      * random deviate.
  206.      */
  207.     private abstract static class BaseStableSampler extends StableSampler {
  208.         /** pi/2 scaled by 2^-53. */
  209.         private static final double PI_2_SCALED = 0x1.0p-54 * Math.PI;
  210.         /** pi/4 scaled by 2^-53. */
  211.         private static final double PI_4_SCALED = 0x1.0p-55 * Math.PI;
  212.         /** -pi / 2. */
  213.         private static final double NEG_PI_2 = -Math.PI / 2;
  214.         /** -pi / 4. */
  215.         private static final double NEG_PI_4 = -Math.PI / 4;

  216.         /** The exponential sampler. */
  217.         private final ContinuousSampler expSampler;

  218.         /**
  219.          * @param rng Underlying source of randomness
  220.          */
  221.         BaseStableSampler(UniformRandomProvider rng) {
  222.             super(rng);
  223.             expSampler = ZigguratSampler.Exponential.of(rng);
  224.         }

  225.         /**
  226.          * Gets a random value for the omega parameter ({@code w}).
  227.          * This is an exponential random variable with mean 1.
  228.          *
  229.          * <p>Warning: For simplicity this does not check the variate is not 0.
  230.          * The calling CMS algorithm should detect and handle incorrect samples as a result
  231.          * of this unlikely edge case.
  232.          *
  233.          * @return omega
  234.          */
  235.         double getOmega() {
  236.             // Note: Ideally this should not have a value of 0 as the CMS algorithm divides
  237.             // by w and it creates infinity. This can result in NaN output.
  238.             // Under certain parameterizations non-zero small w also creates NaN output.
  239.             // Thus output should be checked regardless.
  240.             return expSampler.sample();
  241.         }

  242.         /**
  243.          * Gets a random value for the phi parameter.
  244.          * This is a uniform random variable in {@code (-pi/2, pi/2)}.
  245.          *
  246.          * @return phi
  247.          */
  248.         double getPhi() {
  249.             // See getPhiBy2 for method details.
  250.             final double x = (nextLong() >> 10) * PI_2_SCALED;
  251.             // Deliberate floating-point equality check
  252.             if (x == NEG_PI_2) {
  253.                 return getPhi();
  254.             }
  255.             return x;
  256.         }

  257.         /**
  258.          * Gets a random value for the phi parameter divided by 2.
  259.          * This is a uniform random variable in {@code (-pi/4, pi/4)}.
  260.          *
  261.          * <p>Note: Ideally this should not have a value of -pi/4 or pi/4 as the CMS algorithm
  262.          * can generate infinite values when the phi/2 uniform deviate is +/-pi/4. This
  263.          * can result in NaN output. Under certain parameterizations phi/2 close to the limits
  264.          * also create NaN output. Thus output should be checked regardless. Avoiding
  265.          * the extreme values simplifies the number of checks that are required.
  266.          *
  267.          * @return phi / 2
  268.          */
  269.         double getPhiBy2() {
  270.             // As per o.a.c.rng.core.utils.NumberFactory.makeDouble(long) but using a
  271.             // signed shift of 10 in place of an unsigned shift of 11. With a factor of 2^-53
  272.             // this would produce a double in [-1, 1).
  273.             // Here the multiplication factor incorporates pi/4 to avoid a separate
  274.             // multiplication.
  275.             final double x = (nextLong() >> 10) * PI_4_SCALED;
  276.             // Deliberate floating-point equality check
  277.             if (x == NEG_PI_4) {
  278.                 // Sample again using recursion.
  279.                 // A stack overflow due to a broken RNG will eventually occur
  280.                 // rather than the alternative which is an infinite loop
  281.                 // while x == -pi/4.
  282.                 return getPhiBy2();
  283.             }
  284.             return x;
  285.         }
  286.     }

  287.     /**
  288.      * Class for implementations of a stable distribution transformed by scale and location.
  289.      */
  290.     private static final class TransformedStableSampler extends StableSampler {
  291.         /** Underlying normalized stable sampler. */
  292.         private final StableSampler sampler;
  293.         /** The scale parameter. */
  294.         private final double gamma;
  295.         /** The location parameter. */
  296.         private final double delta;

  297.         /**
  298.          * @param sampler Normalized stable sampler.
  299.          * @param gamma Scale parameter. Must be strictly positive.
  300.          * @param delta Location parameter.
  301.          */
  302.         TransformedStableSampler(StableSampler sampler, double gamma, double delta) {
  303.             // No RNG required
  304.             super(null);
  305.             this.sampler = sampler;
  306.             this.gamma = gamma;
  307.             this.delta = delta;
  308.         }

  309.         @Override
  310.         public double sample() {
  311.             return gamma * sampler.sample() + delta;
  312.         }

  313.         @Override
  314.         public StableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  315.             return new TransformedStableSampler(sampler.withUniformRandomProvider(rng),
  316.                                                 gamma, delta);
  317.         }

  318.         @Override
  319.         public String toString() {
  320.             // Avoid a null pointer from the unset RNG instance in the parent class
  321.             return sampler.toString();
  322.         }
  323.     }

  324.     /**
  325.      * Implement the {@code alpha = 2} stable distribution case (Gaussian distribution).
  326.      */
  327.     private static final class GaussianStableSampler extends StableSampler {
  328.         /** sqrt(2). */
  329.         private static final double ROOT_2 = Math.sqrt(2);

  330.         /** Underlying normalized Gaussian sampler. */
  331.         private final NormalizedGaussianSampler sampler;
  332.         /** The standard deviation. */
  333.         private final double stdDev;
  334.         /** The mean. */
  335.         private final double mean;

  336.         /**
  337.          * @param rng Underlying source of randomness
  338.          * @param gamma Scale parameter. Must be strictly positive.
  339.          * @param delta Location parameter.
  340.          */
  341.         GaussianStableSampler(UniformRandomProvider rng, double gamma, double delta) {
  342.             super(rng);
  343.             this.sampler = ZigguratSampler.NormalizedGaussian.of(rng);
  344.             // A standardized stable sampler with alpha=2 has variance 2.
  345.             // Set the standard deviation as sqrt(2) * scale.
  346.             // Avoid this being infinity to avoid inf * 0 in the sample
  347.             this.stdDev = Math.min(Double.MAX_VALUE, ROOT_2 * gamma);
  348.             this.mean = delta;
  349.         }

  350.         /**
  351.          * @param rng Underlying source of randomness
  352.          * @param source Source to copy.
  353.          */
  354.         GaussianStableSampler(UniformRandomProvider rng, GaussianStableSampler source) {
  355.             super(rng);
  356.             this.sampler = ZigguratSampler.NormalizedGaussian.of(rng);
  357.             this.stdDev = source.stdDev;
  358.             this.mean = source.mean;
  359.         }

  360.         @Override
  361.         public double sample() {
  362.             return stdDev * sampler.sample() + mean;
  363.         }

  364.         @Override
  365.         public GaussianStableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  366.             return new GaussianStableSampler(rng, this);
  367.         }
  368.     }

  369.     /**
  370.      * Implement the {@code alpha = 1} and {@code beta = 0} stable distribution case
  371.      * (Cauchy distribution).
  372.      */
  373.     private static final class CauchyStableSampler extends BaseStableSampler {
  374.         /** The scale parameter. */
  375.         private final double gamma;
  376.         /** The location parameter. */
  377.         private final double delta;

  378.         /**
  379.          * @param rng Underlying source of randomness
  380.          * @param gamma Scale parameter. Must be strictly positive.
  381.          * @param delta Location parameter.
  382.          */
  383.         CauchyStableSampler(UniformRandomProvider rng, double gamma, double delta) {
  384.             super(rng);
  385.             this.gamma = gamma;
  386.             this.delta = delta;
  387.         }

  388.         /**
  389.          * @param rng Underlying source of randomness
  390.          * @param source Source to copy.
  391.          */
  392.         CauchyStableSampler(UniformRandomProvider rng, CauchyStableSampler source) {
  393.             super(rng);
  394.             this.gamma = source.gamma;
  395.             this.delta = source.delta;
  396.         }

  397.         @Override
  398.         public double sample() {
  399.             // Note:
  400.             // The CMS beta=0 with alpha=1 sampler reduces to:
  401.             // S = 2 * a / a2, with a = tan(x), a2 = 1 - a^2, x = phi/2
  402.             // This is a double angle identity for tan:
  403.             // 2 * tan(x) / (1 - tan^2(x)) = tan(2x)
  404.             // Here we use the double angle identity for consistency with the other samplers.
  405.             final double phiby2 = getPhiBy2();
  406.             final double a = phiby2 * SpecialMath.tan2(phiby2);
  407.             final double a2 = 1 - a * a;
  408.             final double x = 2 * a / a2;
  409.             return gamma * x + delta;
  410.         }

  411.         @Override
  412.         public CauchyStableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  413.             return new CauchyStableSampler(rng, this);
  414.         }
  415.     }

  416.     /**
  417.      * Implement the {@code alpha = 0.5} and {@code beta = 1} stable distribution case
  418.      * (Levy distribution).
  419.      *
  420.      * Note: This sampler can be used to output the symmetric case when
  421.      * {@code beta = -1} by negating {@code gamma}.
  422.      */
  423.     private static final class LevyStableSampler extends StableSampler {
  424.         /** Underlying normalized Gaussian sampler. */
  425.         private final NormalizedGaussianSampler sampler;
  426.         /** The scale parameter. */
  427.         private final double gamma;
  428.         /** The location parameter. */
  429.         private final double delta;

  430.         /**
  431.          * @param rng Underlying source of randomness
  432.          * @param gamma Scale parameter. Must be strictly positive.
  433.          * @param delta Location parameter.
  434.          */
  435.         LevyStableSampler(UniformRandomProvider rng, double gamma, double delta) {
  436.             super(rng);
  437.             this.sampler = ZigguratSampler.NormalizedGaussian.of(rng);
  438.             this.gamma = gamma;
  439.             this.delta = delta;
  440.         }

  441.         /**
  442.          * @param rng Underlying source of randomness
  443.          * @param source Source to copy.
  444.          */
  445.         LevyStableSampler(UniformRandomProvider rng, LevyStableSampler source) {
  446.             super(rng);
  447.             this.sampler = ZigguratSampler.NormalizedGaussian.of(rng);
  448.             this.gamma = source.gamma;
  449.             this.delta = source.delta;
  450.         }

  451.         @Override
  452.         public double sample() {
  453.             // Levy(Z) = 1 / N(0,1)^2, where N(0,1) is a standard normalized variate
  454.             final double norm = sampler.sample();
  455.             // Here we must transform from the 1-parameterization to the 0-parameterization.
  456.             // This is a shift of -beta * tan(pi * alpha / 2) = -1 when alpha=0.5, beta=1.
  457.             final double z = (1.0 / (norm * norm)) - 1.0;
  458.             // In the 0-parameterization the scale and location are a linear transform.
  459.             return gamma * z + delta;
  460.         }

  461.         @Override
  462.         public LevyStableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  463.             return new LevyStableSampler(rng, this);
  464.         }
  465.     }

  466.     /**
  467.      * Implement the generic stable distribution case: {@code alpha < 2} and
  468.      * {@code beta != 0}. This routine assumes {@code alpha != 1}.
  469.      *
  470.      * <p>Implements the Chambers-Mallows-Stuck (CMS) method using the
  471.      * formula provided in Weron (1996) "On the Chambers-Mallows-Stuck method for
  472.      * simulating skewed stable random variables" Statistics &amp; Probability
  473.      * Letters. 28 (2): 165–171. This method is easier to correct from infinite and
  474.      * NaN results by boxing intermediate infinite values.
  475.      *
  476.      * <p>The formula produces a stable deviate from the 1-parameterization that is
  477.      * discontinuous at {@code alpha=1}. A shift is used to create the 0-parameterization.
  478.      * This shift is very large as {@code alpha -> 1} and the output loses bits of precision
  479.      * in the deviate due to cancellation. It is not recommended to use this sampler when
  480.      * {@code alpha -> 1} except for edge case correction.
  481.      *
  482.      * <p>This produces non-NaN output for all parameters alpha, beta, u and w with
  483.      * the correct orientation for extremes of the distribution support.
  484.      * The formulas used are symmetric with regard to beta and u.
  485.      *
  486.      * @see <a href="https://doi.org/10.1016%2F0167-7152%2895%2900113-1">Weron, R
  487.      * (1996). Statistics &amp; Probability Letters. 28 (2): 165–171.</a>
  488.      */
  489.     static class WeronStableSampler extends BaseStableSampler {
  490.         /** Epsilon (1 - alpha). */
  491.         protected final double eps;
  492.         /** Alpha (1 - eps). */
  493.         protected final double meps1;
  494.         /** Cache of expression value used in generation. */
  495.         protected final double zeta;
  496.         /** Cache of expression value used in generation. */
  497.         protected final double atanZeta;
  498.         /** Cache of expression value used in generation. */
  499.         protected final double scale;
  500.         /** 1 / alpha = 1 / (1 - eps). */
  501.         protected final double inv1mEps;
  502.         /** (1 / alpha) - 1 = eps / (1 - eps). */
  503.         protected final double epsDiv1mEps;
  504.         /** The inclusive lower support for the distribution. */
  505.         protected final double lower;
  506.         /** The inclusive upper support for the distribution. */
  507.         protected final double upper;

  508.         /**
  509.          * @param rng Underlying source of randomness
  510.          * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  511.          * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  512.          */
  513.         WeronStableSampler(UniformRandomProvider rng, double alpha, double beta) {
  514.             super(rng);
  515.             eps = 1 - alpha;
  516.             // When alpha < 0.5, 1 - eps == alpha is not always true as the reverse is not exact.
  517.             // Here we store 1 - eps in place of alpha. Thus eps + (1 - eps) = 1.
  518.             meps1 = 1 - eps;

  519.             // Compute pre-factors for the Weron formula used during error correction.
  520.             if (meps1 > 1) {
  521.                 // Avoid calling tan outside the domain limit [-pi/2, pi/2].
  522.                 zeta = beta * Math.tan((2 - meps1) * PI_2);
  523.             } else {
  524.                 zeta = -beta * Math.tan(meps1 * PI_2);
  525.             }

  526.             // Do not store xi = Math.atan(-zeta) / meps1 due to floating-point division errors.
  527.             // Directly store Math.atan(-zeta).
  528.             atanZeta = Math.atan(-zeta);
  529.             scale = Math.pow(1 + zeta * zeta, 0.5 / meps1);
  530.             // Note: These terms are used interchangeably in formulas
  531.             //    1         1
  532.             // -------  = -----
  533.             // (1-eps)    alpha
  534.             inv1mEps = 1.0 / meps1;
  535.             //    1             eps     (1-alpha)     1
  536.             // -------  - 1 = ------- = --------- = ----- - 1
  537.             // (1-eps)        (1-eps)     alpha     alpha
  538.             epsDiv1mEps = inv1mEps - 1;


  539.             // Compute the support. This applies when alpha < 1 and beta = +/-1
  540.             if (alpha < 1 && Math.abs(beta) == 1) {
  541.                 if (beta == 1) {
  542.                     // alpha < 0, beta = 1
  543.                     lower = zeta;
  544.                     upper = UPPER;
  545.                 } else {
  546.                     // alpha < 0, beta = -1
  547.                     lower = LOWER;
  548.                     upper = zeta;
  549.                 }
  550.             } else {
  551.                 lower = LOWER;
  552.                 upper = UPPER;
  553.             }
  554.         }

  555.         /**
  556.          * @param rng Underlying source of randomness
  557.          * @param source Source to copy.
  558.          */
  559.         WeronStableSampler(UniformRandomProvider rng, WeronStableSampler source) {
  560.             super(rng);
  561.             this.eps = source.eps;
  562.             this.meps1 = source.meps1;
  563.             this.zeta = source.zeta;
  564.             this.atanZeta = source.atanZeta;
  565.             this.scale = source.scale;
  566.             this.inv1mEps = source.inv1mEps;
  567.             this.epsDiv1mEps = source.epsDiv1mEps;
  568.             this.lower = source.lower;
  569.             this.upper = source.upper;
  570.         }

  571.         @Override
  572.         public double sample() {
  573.             final double phi = getPhi();
  574.             final double w = getOmega();
  575.             return createSample(phi, w);
  576.         }

  577.         /**
  578.          * Create the sample. This routine is robust to edge cases and returns a deviate
  579.          * at the extremes of the support. It correctly handles {@code alpha -> 0} when
  580.          * the sample is increasingly likely to be +/- infinity.
  581.          *
  582.          * @param phi Uniform deviate in {@code (-pi/2, pi/2)}
  583.          * @param w Exponential deviate
  584.          * @return x
  585.          */
  586.         protected double createSample(double phi, double w) {
  587.             // Here we use the formula provided by Weron for the 1-parameterization.
  588.             // Note: Adding back zeta creates the 0-parameterization defined in Nolan (1998):
  589.             // X ~ S0_alpha(s,beta,u0) with s=1, u0=0 for a standard random variable.
  590.             // As alpha -> 1 the translation zeta to create the stable deviate
  591.             // in the 0-parameterization is increasingly large as tan(pi/2) -> infinity.
  592.             // The max translation is approximately 1e16.
  593.             // Without this translation the stable deviate is in the 1-parameterization
  594.             // and the function is not continuous with respect to alpha.
  595.             // Due to the large zeta when alpha -> 1 the number of bits of the output variable
  596.             // are very low due to cancellation.

  597.             // As alpha -> 0 or 2 then zeta -> 0 and cancellation is not relevant.
  598.             // The formula can be modified for infinite terms to compute a result for extreme
  599.             // deviates u and w when the CMS formula fails.

  600.             // Note the following term is subject to floating point error:
  601.             // xi = atan(-zeta) / alpha
  602.             // alphaPhiXi = alpha * (phi + xi)
  603.             // This is required: cos(phi - alphaPhiXi) > 0 => phi - alphaPhiXi in (-pi/2, pi/2).
  604.             // Thus we compute atan(-zeta) and use it to compute two terms:
  605.             // [1] alpha * (phi + xi) = alpha * (phi + atan(-zeta) / alpha) = alpha * phi + atan(-zeta)
  606.             // [2] phi - alpha * (phi + xi) = phi - alpha * phi - atan(-zeta) = (1-alpha) * phi - atan(-zeta)

  607.             // Compute terms
  608.             // Either term can be infinite or 0. Certain parameters compute 0 * inf.
  609.             // t1=inf occurs alpha -> 0.
  610.             // t1=0 occurs when beta = tan(-alpha * phi) / tan(alpha * pi / 2).
  611.             // t2=inf occurs when w -> 0 and alpha -> 0.
  612.             // t2=0 occurs when alpha -> 0 and phi -> pi/2.
  613.             // Detect zeros and return as zeta.

  614.             // Note sin(alpha * phi + atanZeta) is zero when:
  615.             // alpha * phi = -atan(-zeta)
  616.             // tan(-alpha * phi) = -zeta
  617.             //                   = beta * tan(alpha * pi / 2)
  618.             // Since |phi| < pi/2 this requires beta to have an opposite sign to phi
  619.             // and a magnitude < 1. This is possible and in this case avoid a possible
  620.             // 0 / 0 by setting the result as if term t1=0 and the result is zeta.
  621.             double t1 = Math.sin(meps1 * phi + atanZeta);
  622.             if (t1 == 0) {
  623.                 return zeta;
  624.             }
  625.             // Since cos(phi) is in (0, 1] this term will not create a
  626.             // large magnitude to create t1 = 0.
  627.             t1 /= Math.pow(Math.cos(phi), inv1mEps);

  628.             // Iff Math.cos(eps * phi - atanZeta) is zero then 0 / 0 can occur if w=0.
  629.             // Iff Math.cos(eps * phi - atanZeta) is below zero then NaN will occur
  630.             // in the power function. These cases are avoided by phi=(-pi/2, pi/2) and direct
  631.             // use of arctan(-zeta).
  632.             final double t2 = Math.pow(Math.cos(eps * phi - atanZeta) / w, epsDiv1mEps);
  633.             if (t2 == 0) {
  634.                 return zeta;
  635.             }

  636.             final double x = t1 * t2 * scale + zeta;

  637.             // Check the bounds. Applies when alpha < 1 and beta = +/-1.
  638.             if (x <= lower) {
  639.                 return lower;
  640.             }
  641.             return x < upper ? x : upper;
  642.         }

  643.         @Override
  644.         public WeronStableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  645.             return new WeronStableSampler(rng, this);
  646.         }
  647.     }

  648.     /**
  649.      * Implement the generic stable distribution case: {@code alpha < 2} and
  650.      * {@code beta != 0}. This routine assumes {@code alpha != 1}.
  651.      *
  652.      * <p>Implements the Chambers-Mallows-Stuck (CMS) method from Chambers, et al
  653.      * (1976) A Method for Simulating Stable Random Variables. Journal of the
  654.      * American Statistical Association Vol. 71, No. 354, pp. 340-344.
  655.      *
  656.      * <p>The formula produces a stable deviate from the 0-parameterization that is
  657.      * continuous at {@code alpha=1}.
  658.      *
  659.      * <p>This is an implementation of the Fortran routine RSTAB. In the event the
  660.      * computation fails then an alternative computation is performed using the
  661.      * formula provided in Weron (1996) "On the Chambers-Mallows-Stuck method for
  662.      * simulating skewed stable random variables" Statistics &amp; Probability
  663.      * Letters. 28 (2): 165–171. This method is easier to correct from infinite and
  664.      * NaN results. The error correction path is extremely unlikely to occur during
  665.      * use unless {@code alpha -> 0}. In general use it requires the random deviates
  666.      * w or u are extreme. See the unit tests for conditions that create them.
  667.      *
  668.      * <p>This produces non-NaN output for all parameters alpha, beta, u and w with
  669.      * the correct orientation for extremes of the distribution support.
  670.      * The formulas used are symmetric with regard to beta and u.
  671.      */
  672.     static class CMSStableSampler extends WeronStableSampler {
  673.         /** 1/2. */
  674.         private static final double HALF = 0.5;
  675.         /** Cache of expression value used in generation. */
  676.         private final double tau;

  677.         /**
  678.          * @param rng Underlying source of randomness
  679.          * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  680.          * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  681.          */
  682.         CMSStableSampler(UniformRandomProvider rng, double alpha, double beta) {
  683.             super(rng, alpha, beta);

  684.             // Compute the RSTAB pre-factor.
  685.             tau = getTau(alpha, beta);
  686.         }

  687.         /**
  688.          * @param rng Underlying source of randomness
  689.          * @param source Source to copy.
  690.          */
  691.         CMSStableSampler(UniformRandomProvider rng, CMSStableSampler source) {
  692.             super(rng, source);
  693.             this.tau = source.tau;
  694.         }

  695.         /**
  696.          * Gets tau. This is a factor used in the CMS algorithm. If this is zero then
  697.          * a special case of {@code beta -> 0} has occurred.
  698.          *
  699.          * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  700.          * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  701.          * @return tau
  702.          */
  703.         static double getTau(double alpha, double beta) {
  704.             final double eps = 1 - alpha;
  705.             final double meps1 = 1 - eps;
  706.             // Compute RSTAB prefactor
  707.             final double tau;

  708.             // tau is symmetric around alpha=1
  709.             // tau -> beta / pi/2 as alpha -> 1
  710.             // tau -> 0 as alpha -> 2 or 0
  711.             // Avoid calling tan as the value approaches the domain limit [-pi/2, pi/2].
  712.             if (Math.abs(eps) < HALF) {
  713.                 // 0.5 < alpha < 1.5. Note: This works when eps=0 as tan(0) / 0 == 1.
  714.                 tau = beta / (SpecialMath.tan2(eps * PI_2) * PI_2);
  715.             } else {
  716.                 // alpha >= 1.5 or alpha <= 0.5.
  717.                 // Do not call tan with alpha > 1 as it wraps in the domain [-pi/2, pi/2].
  718.                 // Since pi is approximate the symmetry is lost by wrapping.
  719.                 // Keep within the domain using (2-alpha).
  720.                 if (meps1 > 1) {
  721.                     tau = beta * PI_2 * eps * (2 - meps1) * -SpecialMath.tan2((2 - meps1) * PI_2);
  722.                 } else {
  723.                     tau = beta * PI_2 * eps * meps1 * SpecialMath.tan2(meps1 * PI_2);
  724.                 }
  725.             }

  726.             return tau;
  727.         }

  728.         @Override
  729.         public double sample() {
  730.             final double phiby2 = getPhiBy2();
  731.             final double w = getOmega();

  732.             // Compute as per the RSTAB routine.

  733.             // Generic stable distribution that is continuous as alpha -> 1.
  734.             // This is a trigonomic rearrangement of equation 4.1 from Chambers et al (1976)
  735.             // as implemented in the Fortran program RSTAB.
  736.             // Uses the special functions:
  737.             // tan2 = tan(x) / x
  738.             // d2 = (exp(x) - 1) / x
  739.             // The method is implemented as per the RSTAB routine with the exceptions:
  740.             // 1. The function tan2(x) is implemented with a higher precision approximation.
  741.             // 2. The sample is tested against the expected distribution support.
  742.             // Infinite intermediate terms that create infinite or NaN are corrected by
  743.             // switching the formula and handling infinite terms.

  744.             // Compute some tangents
  745.             // a in (-1, 1)
  746.             // bb in [1, 4/pi)
  747.             // b in (-1, 1)
  748.             final double a = phiby2 * SpecialMath.tan2(phiby2);
  749.             final double bb = SpecialMath.tan2(eps * phiby2);
  750.             final double b = eps * phiby2 * bb;

  751.             // Compute some necessary subexpressions
  752.             final double da = a * a;
  753.             final double db = b * b;
  754.             // a2 in (0, 1]
  755.             final double a2 = 1 - da;
  756.             // a2p in [1, 2)
  757.             final double a2p = 1 + da;
  758.             // b2 in (0, 1]
  759.             final double b2 = 1 - db;
  760.             // b2p in [1, 2)
  761.             final double b2p = 1 + db;

  762.             // Compute coefficient.
  763.             // numerator=0 is not possible *in theory* when the uniform deviate generating phi
  764.             // is in the open interval (0, 1). In practice it is possible to obtain <=0 due
  765.             // to round-off error, typically when beta -> +/-1 and phiby2 -> -/+pi/4.
  766.             // This can happen for any alpha.
  767.             final double z = a2p * (b2 + 2 * phiby2 * bb * tau) / (w * a2 * b2p);

  768.             // Compute the exponential-type expression
  769.             // Note: z may be infinite, typically when w->0 and a2->0.
  770.             // This can produce NaN under certain parameterizations due to multiplication by 0.
  771.             final double alogz = Math.log(z);
  772.             final double d = SpecialMath.d2(epsDiv1mEps * alogz) * (alogz * inv1mEps);

  773.             // Pre-compute the multiplication factor.
  774.             // The numerator may be zero. The denominator is not zero as a2 is bounded to
  775.             // above zero when the uniform deviate that generates phiby2 is not 0 or 1.
  776.             // The min value of a2 is 2^-52. Assume f cannot be infinite as the numerator
  777.             // is computed with a in (-1, 1); b in (-1, 1); phiby2 in (-pi/4, pi/4); tau in
  778.             // [-2/pi, 2/pi]; bb in [1, 4/pi); a2 in (0, 1] limiting the numerator magnitude.
  779.             final double f = (2 * ((a - b) * (1 + a * b) - phiby2 * tau * bb * (b * a2 - 2 * a))) /
  780.                     (a2 * b2p);

  781.             // Compute the stable deviate:
  782.             final double x = (1 + eps * d) * f + tau * d;

  783.             // Test the support
  784.             if (lower < x && x < upper) {
  785.                 return x;
  786.             }

  787.             // Error correction path:
  788.             // x is at the bounds, infinite or NaN (created by 0 / 0,  0 * inf, or inf - inf).
  789.             // This is caused by extreme parameterizations of alpha or beta, or extreme values
  790.             // from the random deviates.
  791.             // Alternatively alpha < 1 and beta = +/-1 and the sample x is at the edge or
  792.             // outside the support due to floating point error.

  793.             // Here we use the formula provided by Weron which is easier to correct
  794.             // when deviates are extreme or alpha -> 0. The formula is not continuous
  795.             // as alpha -> 1 without a shift which reduces the precision of the sample;
  796.             // for rare edge case correction this has minimal effect on sampler output.
  797.             return createSample(phiby2 * 2, w);
  798.         }

  799.         @Override
  800.         public CMSStableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  801.             return new CMSStableSampler(rng, this);
  802.         }
  803.     }

  804.     /**
  805.      * Implement the stable distribution case: {@code alpha == 1} and {@code beta != 0}.
  806.      *
  807.      * <p>Implements the same algorithm as the {@link CMSStableSampler} with
  808.      * the {@code alpha} assumed to be 1.
  809.      *
  810.      * <p>This sampler specifically requires that {@code beta / (pi/2) != 0}; otherwise
  811.      * the parameters equal {@code alpha=1, beta=0} as the Cauchy distribution case.
  812.      */
  813.     static class Alpha1CMSStableSampler extends BaseStableSampler {
  814.         /** Cache of expression value used in generation. */
  815.         private final double tau;

  816.         /**
  817.          * @param rng Underlying source of randomness
  818.          * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  819.          */
  820.         Alpha1CMSStableSampler(UniformRandomProvider rng, double beta) {
  821.             super(rng);
  822.             tau = beta / PI_2;
  823.         }

  824.         /**
  825.          * @param rng Underlying source of randomness
  826.          * @param source Source to copy.
  827.          */
  828.         Alpha1CMSStableSampler(UniformRandomProvider rng, Alpha1CMSStableSampler source) {
  829.             super(rng);
  830.             this.tau = source.tau;
  831.         }

  832.         @Override
  833.         public double sample() {
  834.             final double phiby2 = getPhiBy2();
  835.             final double w = getOmega();

  836.             // Compute some tangents
  837.             final double a = phiby2 * SpecialMath.tan2(phiby2);

  838.             // Compute some necessary subexpressions
  839.             final double da = a * a;
  840.             final double a2 = 1 - da;
  841.             final double a2p = 1 + da;

  842.             // Compute coefficient.

  843.             // numerator=0 is not possible when the uniform deviate generating phi
  844.             // is in the open interval (0, 1) and alpha=1.
  845.             final double z = a2p * (1 + 2 * phiby2 * tau) / (w * a2);

  846.             // Compute the exponential-type expression
  847.             // Note: z may be infinite, typically when w->0 and a2->0.
  848.             // This can produce NaN under certain parameterizations due to multiplication by 0.
  849.             // When alpha=1 the expression
  850.             // d = d2((eps / (1-eps)) * alogz) * (alogz / (1-eps)) is eliminated to 1 * log(z)
  851.             final double d = Math.log(z);

  852.             // Pre-compute the multiplication factor.
  853.             final double f = (2 * (a - phiby2 * tau * (-2 * a))) / a2;

  854.             // Compute the stable deviate:
  855.             // This does not require correction as f is finite (as per the alpha != 1 case),
  856.             // tau is non-zero and only d can be infinite due to an extreme w -> 0.
  857.             return f + tau * d;
  858.         }

  859.         @Override
  860.         public Alpha1CMSStableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  861.             return new Alpha1CMSStableSampler(rng, this);
  862.         }
  863.     }

  864.     /**
  865.      * Implement the generic stable distribution case: {@code alpha < 2} and {@code beta == 0}.
  866.      *
  867.      * <p>Implements the same algorithm as the {@link WeronStableSampler} with
  868.      * the {@code beta} assumed to be 0.
  869.      *
  870.      * <p>This routine assumes {@code alpha != 1}; {@code alpha=1, beta=0} is the Cauchy
  871.      * distribution case.
  872.      */
  873.     static class Beta0WeronStableSampler extends BaseStableSampler {
  874.         /** Epsilon (1 - alpha). */
  875.         protected final double eps;
  876.         /** Epsilon (1 - alpha). */
  877.         protected final double meps1;
  878.         /** 1 / alpha = 1 / (1 - eps). */
  879.         protected final double inv1mEps;
  880.         /** (1 / alpha) - 1 = eps / (1 - eps). */
  881.         protected final double epsDiv1mEps;

  882.         /**
  883.          * @param rng Underlying source of randomness
  884.          * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  885.          */
  886.         Beta0WeronStableSampler(UniformRandomProvider rng, double alpha) {
  887.             super(rng);
  888.             eps = 1 - alpha;
  889.             meps1 = 1 - eps;
  890.             inv1mEps = 1.0 / meps1;
  891.             epsDiv1mEps = inv1mEps - 1;
  892.         }

  893.         /**
  894.          * @param rng Underlying source of randomness
  895.          * @param source Source to copy.
  896.          */
  897.         Beta0WeronStableSampler(UniformRandomProvider rng, Beta0WeronStableSampler source) {
  898.             super(rng);
  899.             this.eps = source.eps;
  900.             this.meps1 = source.meps1;
  901.             this.inv1mEps = source.inv1mEps;
  902.             this.epsDiv1mEps = source.epsDiv1mEps;
  903.         }

  904.         @Override
  905.         public double sample() {
  906.             final double phi = getPhi();
  907.             final double w = getOmega();
  908.             return createSample(phi, w);
  909.         }

  910.         /**
  911.          * Create the sample. This routine is robust to edge cases and returns a deviate
  912.          * at the extremes of the support. It correctly handles {@code alpha -> 0} when
  913.          * the sample is increasingly likely to be +/- infinity.
  914.          *
  915.          * @param phi Uniform deviate in {@code (-pi/2, pi/2)}
  916.          * @param w Exponential deviate
  917.          * @return x
  918.          */
  919.         protected double createSample(double phi, double w) {
  920.             // As per the Weron sampler with beta=0 and terms eliminated.
  921.             // Note that if alpha=1 this reduces to sin(phi) / cos(phi) => Cauchy case.

  922.             // Compute terms.
  923.             // Either term can be infinite or 0. Certain parameters compute 0 * inf.
  924.             // Detect zeros and return as 0.

  925.             // Note sin(alpha * phi) is only ever zero when phi=0. No value of alpha
  926.             // multiplied by small phi can create zero due to the limited
  927.             // precision of alpha imposed by alpha = 1 - (1-alpha). At this point cos(phi) = 1.
  928.             // Thus 0/0 cannot occur.
  929.             final double t1 = Math.sin(meps1 * phi) / Math.pow(Math.cos(phi), inv1mEps);
  930.             if (t1 == 0) {
  931.                 return 0;
  932.             }
  933.             final double t2 = Math.pow(Math.cos(eps * phi) / w, epsDiv1mEps);
  934.             if (t2 == 0) {
  935.                 return 0;
  936.             }
  937.             return t1 * t2;
  938.         }

  939.         @Override
  940.         public Beta0WeronStableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  941.             return new Beta0WeronStableSampler(rng, this);
  942.         }
  943.     }

  944.     /**
  945.      * Implement the generic stable distribution case: {@code alpha < 2} and {@code beta == 0}.
  946.      *
  947.      * <p>Implements the same algorithm as the {@link CMSStableSampler} with
  948.      * the {@code beta} assumed to be 0.
  949.      *
  950.      * <p>This routine assumes {@code alpha != 1}; {@code alpha=1, beta=0} is the Cauchy
  951.      * distribution case.
  952.      */
  953.     static class Beta0CMSStableSampler extends Beta0WeronStableSampler {
  954.         /**
  955.          * @param rng Underlying source of randomness
  956.          * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  957.          */
  958.         Beta0CMSStableSampler(UniformRandomProvider rng, double alpha) {
  959.             super(rng, alpha);
  960.         }

  961.         /**
  962.          * @param rng Underlying source of randomness
  963.          * @param source Source to copy.
  964.          */
  965.         Beta0CMSStableSampler(UniformRandomProvider rng, Beta0CMSStableSampler source) {
  966.             super(rng, source);
  967.         }

  968.         @Override
  969.         public double sample() {
  970.             final double phiby2 = getPhiBy2();
  971.             final double w = getOmega();

  972.             // Compute some tangents
  973.             final double a = phiby2 * SpecialMath.tan2(phiby2);
  974.             final double b = eps * phiby2 * SpecialMath.tan2(eps * phiby2);
  975.             // Compute some necessary subexpressions
  976.             final double da = a * a;
  977.             final double db = b * b;
  978.             final double a2 = 1 - da;
  979.             final double a2p = 1 + da;
  980.             final double b2 = 1 - db;
  981.             final double b2p = 1 + db;
  982.             // Compute coefficient.
  983.             final double z = a2p * b2 / (w * a2 * b2p);

  984.             // Compute the exponential-type expression
  985.             final double alogz = Math.log(z);
  986.             final double d = SpecialMath.d2(epsDiv1mEps * alogz) * (alogz * inv1mEps);

  987.             // Pre-compute the multiplication factor.
  988.             // The numerator may be zero. The denominator is not zero as a2 is bounded to
  989.             // above zero when the uniform deviate that generates phiby2 is not 0 or 1.
  990.             final double f = (2 * ((a - b) * (1 + a * b))) / (a2 * b2p);

  991.             // Compute the stable deviate:
  992.             final double x = (1 + eps * d) * f;

  993.             // Test the support
  994.             if (LOWER < x && x < UPPER) {
  995.                 return x;
  996.             }

  997.             // Error correction path.
  998.             // Here we use the formula provided by Weron which is easier to correct
  999.             // when deviates are extreme or alpha -> 0.
  1000.             return createSample(phiby2 * 2, w);
  1001.         }

  1002.         @Override
  1003.         public Beta0CMSStableSampler withUniformRandomProvider(UniformRandomProvider rng) {
  1004.             return new Beta0CMSStableSampler(rng, this);
  1005.         }
  1006.     }

  1007.     /**
  1008.      * Implement special math functions required by the CMS algorithm.
  1009.      */
  1010.     static final class SpecialMath {
  1011.         /** pi/4. */
  1012.         private static final double PI_4 = Math.PI / 4;
  1013.         /** 4/pi. */
  1014.         private static final double FOUR_PI = 4 / Math.PI;
  1015.         /** tan2 product constant. */
  1016.         private static final double P0 = -0.5712939549476836914932149599e10;
  1017.         /** tan2 product constant. */
  1018.         private static final double P1 = 0.4946855977542506692946040594e9;
  1019.         /** tan2 product constant. */
  1020.         private static final double P2 = -0.9429037070546336747758930844e7;
  1021.         /** tan2 product constant. */
  1022.         private static final double P3 = 0.5282725819868891894772108334e5;
  1023.         /** tan2 product constant. */
  1024.         private static final double P4 = -0.6983913274721550913090621370e2;
  1025.         /** tan2 quotient constant. */
  1026.         private static final double Q0 = -0.7273940551075393257142652672e10;
  1027.         /** tan2 quotient constant. */
  1028.         private static final double Q1 = 0.2125497341858248436051062591e10;
  1029.         /** tan2 quotient constant. */
  1030.         private static final double Q2 = -0.8000791217568674135274814656e8;
  1031.         /** tan2 quotient constant. */
  1032.         private static final double Q3 = 0.8232855955751828560307269007e6;
  1033.         /** tan2 quotient constant. */
  1034.         private static final double Q4 = -0.2396576810261093558391373322e4;
  1035.         /**
  1036.          * The threshold to switch to using {@link Math#expm1(double)}. The following
  1037.          * table shows the mean (max) ULP difference between using expm1 and exp using
  1038.          * random +/-x with different exponents (n=2^30):
  1039.          *
  1040.          * <pre>
  1041.          * x        exp  positive x                 negative x
  1042.          * 64.0      6   0.10004021506756544  (2)   0.0                   (0)
  1043.          * 32.0      5   0.11177831795066595  (2)   0.0                   (0)
  1044.          * 16.0      4   0.0986650362610817   (2)   9.313225746154785E-10 (1)
  1045.          * 8.0       3   0.09863092936575413  (2)   4.9658119678497314E-6 (1)
  1046.          * 4.0       2   0.10015273280441761  (2)   4.547201097011566E-4  (1)
  1047.          * 2.0       1   0.14359260816127062  (2)   0.005623611621558666  (2)
  1048.          * 1.0       0   0.20160607434809208  (2)   0.03312791418284178   (2)
  1049.          * 0.5      -1   0.3993037799373269   (2)   0.28186883218586445   (2)
  1050.          * 0.25     -2   0.6307008266448975   (2)   0.5192863345146179    (2)
  1051.          * 0.125    -3   1.3862918205559254   (4)   1.386285437270999     (4)
  1052.          * 0.0625   -4   2.772640804760158    (8)   2.772612397558987     (8)
  1053.          * </pre>
  1054.          *
  1055.          * <p>The threshold of 0.5 has a mean ULP below 0.5 and max ULP of 2. The
  1056.          * transition is monotonic. Neither is true for the next threshold of 0.25.
  1057.          */
  1058.         private static final double SWITCH_TO_EXPM1 = 0.5;

  1059.         /** No instances. */
  1060.         private SpecialMath() {}

  1061.         /**
  1062.          * Evaluate {@code (exp(x) - 1) / x}. For {@code x} in the range {@code [-inf, inf]} returns
  1063.          * a result in {@code [0, inf]}.
  1064.          *
  1065.          * <ul>
  1066.          * <li>For {@code x=-inf} this returns {@code 0}.
  1067.          * <li>For {@code x=0} this returns {@code 1}.
  1068.          * <li>For {@code x=inf} this returns {@code inf}.
  1069.          * <li>For {@code x=nan} this returns {@code nan}.
  1070.          * </ul>
  1071.          *
  1072.          * <p> This corrects {@code 0 / 0} and {@code inf / inf} division from
  1073.          * {@code NaN} to either {@code 1} or the upper bound respectively.
  1074.          *
  1075.          * @param x value to evaluate
  1076.          * @return {@code (exp(x) - 1) / x}.
  1077.          */
  1078.         static double d2(double x) {
  1079.             // Here expm1 is only used when use of expm1 and exp consistently
  1080.             // compute different results by more than 0.5 ULP.
  1081.             if (Math.abs(x) < SWITCH_TO_EXPM1) {
  1082.                 // Deliberate comparison to floating-point zero
  1083.                 if (x == 0) {
  1084.                     // Avoid 0 / 0 error
  1085.                     return 1.0;
  1086.                 }
  1087.                 return Math.expm1(x) / x;
  1088.             }
  1089.             // No use of expm1. Accuracy as x moves away from 0 is not required as the result
  1090.             // is divided by x and the accuracy of the final result is within a few ULP.
  1091.             if (x < Double.POSITIVE_INFINITY) {
  1092.                 return (Math.exp(x) - 1) / x;
  1093.             }
  1094.             // Upper bound (or NaN)
  1095.             return x;
  1096.         }

  1097.         /**
  1098.          * Evaluate {@code tan(x) / x}.
  1099.          *
  1100.          * <p>For {@code x} in the range {@code [0, pi/4]} this returns
  1101.          * a value in the range {@code [1, 4 / pi]}.
  1102.          *
  1103.          * <p>The following properties are desirable for the CMS algorithm:
  1104.          *
  1105.          * <ul>
  1106.          * <li>For {@code x=0} this returns {@code 1}.
  1107.          * <li>For {@code x=pi/4} this returns {@code 4/pi}.
  1108.          * <li>For {@code x=pi/4} this multiplied by {@code x} returns {@code 1}.
  1109.          * </ul>
  1110.          *
  1111.          * <p>This method is called by the CMS algorithm when {@code x < pi/4}.
  1112.          * In this case the method is almost as accurate as {@code Math.tan(x) / x}, does
  1113.          * not require checking for {@code x=0} and is faster.
  1114.          *
  1115.          * @param x the x
  1116.          * @return {@code tan(x) / x}.
  1117.          */
  1118.         static double tan2(double x) {
  1119.             if (Math.abs(x) > PI_4) {
  1120.                 // Reduction is not supported. Delegate to the JDK.
  1121.                 return Math.tan(x) / x;
  1122.             }

  1123.             // Testing with approximation 4283 from Hart et al, as used in the RSTAB
  1124.             // routine, showed the method was not accurate enough for use with
  1125.             // double computation. Hart et al state it has max relative error = 1e-10.66.
  1126.             // For tan(x) / x with x in [0, pi/4] values outside [1, 4/pi] were computed.
  1127.             // When testing verses Math.tan(x) the mean ULP difference is 93436.3.

  1128.             // Approximation 4288 from Hart et al (1968, P. 252).
  1129.             // Max relative error = 1e-26.68 (for tan(x)).
  1130.             // When testing verses Math.tan(x) the mean ULP difference is 0.590597.

  1131.             // The approximation is defined as:
  1132.             // tan(x*pi/4) = x * P(x^2) / Q(x^2)
  1133.             //   with P and Q polynomials of x squared.
  1134.             //
  1135.             // To create tan(x):
  1136.             // tan(x) = xi * P(xi^2) / Q(xi^2), xi = x * 4/pi
  1137.             // tan(x) / x = xi * P(xi^2) / Q(xi^2) / x
  1138.             // tan(x) / x = 4/pi * (P(xi^2) / Q(xi^2))
  1139.             //            = P(xi^2) / (pi/4 * Q(xi^2))
  1140.             // The later has a smaller mean ULP difference to Math.tan(x) / x.
  1141.             final double xi = x * FOUR_PI;

  1142.             // Use the power form with a reverse summation order to have smaller
  1143.             // magnitude terms first. Note: x < 1 so greater powers are smaller.
  1144.             // This has essentially the same accuracy as the nested form of the polynomials
  1145.             // for a marginal performance increase. See JMH examples for performance tests.
  1146.             final double x2 = xi * xi;
  1147.             final double x4 = x2 * x2;
  1148.             final double x6 = x4 * x2;
  1149.             final double x8 = x4 * x4;
  1150.             return          (x8 * P4 + x6 * P3 + x4 * P2 + x2 * P1 + P0) /
  1151.                     (PI_4 * (x8 * x2 + x8 * Q4 + x6 * Q3 + x4 * Q2 + x2 * Q1 + Q0));
  1152.         }
  1153.     }

  1154.     /**
  1155.      * @param rng Generator of uniformly distributed random numbers.
  1156.      */
  1157.     StableSampler(UniformRandomProvider rng) {
  1158.         this.rng = rng;
  1159.     }

  1160.     /**
  1161.      * Generate a sample from a stable distribution.
  1162.      *
  1163.      * <p>The distribution uses the 0-parameterization: S(alpha, beta, gamma, delta; 0).
  1164.      */
  1165.     @Override
  1166.     public abstract double sample();

  1167.     /** {@inheritDoc} */
  1168.     // Redeclare the signature to return a StableSampler not a SharedStateContinuousSampler
  1169.     @Override
  1170.     public abstract StableSampler withUniformRandomProvider(UniformRandomProvider rng);

  1171.     /**
  1172.      * Generates a {@code long} value.
  1173.      * Used by algorithm implementations without exposing access to the RNG.
  1174.      *
  1175.      * @return the next random value
  1176.      */
  1177.     long nextLong() {
  1178.         return rng.nextLong();
  1179.     }

  1180.     /** {@inheritDoc} */
  1181.     @Override
  1182.     public String toString() {
  1183.         // All variations use the same string representation, i.e. no changes
  1184.         // for the Gaussian, Levy or Cauchy case.
  1185.         return "Stable deviate [" + rng.toString() + "]";
  1186.     }

  1187.     /**
  1188.      * Creates a standardized sampler of a stable distribution with zero location and unit scale.
  1189.      *
  1190.      * <p>Special cases:
  1191.      *
  1192.      * <ul>
  1193.      * <li>{@code alpha=2} returns a Gaussian distribution sampler with
  1194.      *     {@code mean=0} and {@code variance=2} (Note: {@code beta} has no effect on the distribution).
  1195.      * <li>{@code alpha=1} and {@code beta=0} returns a Cauchy distribution sampler with
  1196.      *     {@code location=0} and {@code scale=1}.
  1197.      * <li>{@code alpha=0.5} and {@code beta=1} returns a Levy distribution sampler with
  1198.      *     {@code location=-1} and {@code scale=1}. This location shift is due to the
  1199.      *     0-parameterization of the stable distribution.
  1200.      * </ul>
  1201.      *
  1202.      * <p>Note: To allow the computation of the stable distribution the parameter alpha
  1203.      * is validated using {@code 1 - alpha} in the interval {@code [-1, 1)}.
  1204.      *
  1205.      * @param rng Generator of uniformly distributed random numbers.
  1206.      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  1207.      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  1208.      * @return the sampler
  1209.      * @throws IllegalArgumentException if {@code 1 - alpha < -1}; or {@code 1 - alpha >= 1};
  1210.      * or {@code beta < -1}; or {@code beta > 1}.
  1211.      */
  1212.     public static StableSampler of(UniformRandomProvider rng,
  1213.                                    double alpha,
  1214.                                    double beta) {
  1215.         validateParameters(alpha, beta);
  1216.         return create(rng, alpha, beta);
  1217.     }

  1218.     /**
  1219.      * Creates a sampler of a stable distribution. This applies a transformation to the
  1220.      * standardized sampler.
  1221.      *
  1222.      * <p>The random variable \( X \) has
  1223.      * the stable distribution \( S(\alpha, \beta, \gamma, \delta; 0) \) if:
  1224.      *
  1225.      * <p>\[ X = \gamma Z_0 + \delta \]
  1226.      *
  1227.      * <p>where \( Z_0 = S(\alpha, \beta; 0) \) is a standardized stable distribution.
  1228.      *
  1229.      * <p>Note: To allow the computation of the stable distribution the parameter alpha
  1230.      * is validated using {@code 1 - alpha} in the interval {@code [-1, 1)}.
  1231.      *
  1232.      * @param rng Generator of uniformly distributed random numbers.
  1233.      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  1234.      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  1235.      * @param gamma Scale parameter. Must be strictly positive and finite.
  1236.      * @param delta Location parameter. Must be finite.
  1237.      * @return the sampler
  1238.      * @throws IllegalArgumentException if {@code 1 - alpha < -1}; or {@code 1 - alpha >= 1};
  1239.      * or {@code beta < -1}; or {@code beta > 1}; or {@code gamma <= 0}; or
  1240.      * {@code gamma} or {@code delta} are not finite.
  1241.      * @see #of(UniformRandomProvider, double, double)
  1242.      */
  1243.     public static StableSampler of(UniformRandomProvider rng,
  1244.                                    double alpha,
  1245.                                    double beta,
  1246.                                    double gamma,
  1247.                                    double delta) {
  1248.         validateParameters(alpha, beta, gamma, delta);

  1249.         // Choose the algorithm.
  1250.         // Reuse the special cases as they have transformation support.

  1251.         if (alpha == ALPHA_GAUSSIAN) {
  1252.             // Note: beta has no effect and is ignored.
  1253.             return new GaussianStableSampler(rng, gamma, delta);
  1254.         }

  1255.         // Note: As beta -> 0 the result cannot be computed differently to beta = 0.
  1256.         if (alpha == ALPHA_CAUCHY && CMSStableSampler.getTau(ALPHA_CAUCHY, beta) == TAU_ZERO) {
  1257.             return new CauchyStableSampler(rng, gamma, delta);
  1258.         }

  1259.         if (alpha == ALPHA_LEVY && Math.abs(beta) == BETA_LEVY) {
  1260.             // Support mirroring for negative beta by inverting the beta=1 Levy sample
  1261.             // using a negative gamma. Note: The delta is not mirrored as it is a shift
  1262.             // applied to the scaled and mirrored distribution.
  1263.             return new LevyStableSampler(rng, beta * gamma, delta);
  1264.         }

  1265.         // Standardized sampler
  1266.         final StableSampler sampler = create(rng, alpha, beta);
  1267.         // Transform
  1268.         return new TransformedStableSampler(sampler, gamma, delta);
  1269.     }

  1270.     /**
  1271.      * Creates a standardized sampler of a stable distribution with zero location and unit scale.
  1272.      *
  1273.      * @param rng Generator of uniformly distributed random numbers.
  1274.      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  1275.      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  1276.      * @return the sampler
  1277.      */
  1278.     private static StableSampler create(UniformRandomProvider rng,
  1279.                                         double alpha,
  1280.                                         double beta) {
  1281.         // Choose the algorithm.
  1282.         // The special case samplers have transformation support and use gamma=1.0, delta=0.0.
  1283.         // As alpha -> 0 the computation increasingly requires correction
  1284.         // of infinity to the distribution support.

  1285.         if (alpha == ALPHA_GAUSSIAN) {
  1286.             // Note: beta has no effect and is ignored.
  1287.             return new GaussianStableSampler(rng, GAMMA_1, DELTA_0);
  1288.         }

  1289.         // Note: As beta -> 0 the result cannot be computed differently to beta = 0.
  1290.         // This is based on the computation factor tau:
  1291.         final double tau = CMSStableSampler.getTau(alpha, beta);

  1292.         if (tau == TAU_ZERO) {
  1293.             // Symmetric case (beta skew parameter is effectively zero)
  1294.             if (alpha == ALPHA_CAUCHY) {
  1295.                 return new CauchyStableSampler(rng, GAMMA_1, DELTA_0);
  1296.             }
  1297.             if (alpha <= ALPHA_SMALL) {
  1298.                 // alpha -> 0 requires robust error correction
  1299.                 return new Beta0WeronStableSampler(rng, alpha);
  1300.             }
  1301.             return new Beta0CMSStableSampler(rng, alpha);
  1302.         }

  1303.         // Here beta is significant.

  1304.         if (alpha == 1) {
  1305.             return new Alpha1CMSStableSampler(rng, beta);
  1306.         }

  1307.         if (alpha == ALPHA_LEVY && Math.abs(beta) == BETA_LEVY) {
  1308.             // Support mirroring for negative beta by inverting the beta=1 Levy sample
  1309.             // using a negative gamma. Note: The delta is not mirrored as it is a shift
  1310.             // applied to the scaled and mirrored distribution.
  1311.             return new LevyStableSampler(rng, beta, DELTA_0);
  1312.         }

  1313.         if (alpha <= ALPHA_SMALL) {
  1314.             // alpha -> 0 requires robust error correction
  1315.             return new WeronStableSampler(rng, alpha, beta);
  1316.         }

  1317.         return new CMSStableSampler(rng, alpha, beta);
  1318.     }

  1319.     /**
  1320.      * Validate the parameters are in the correct range.
  1321.      *
  1322.      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  1323.      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  1324.      * @throws IllegalArgumentException if {@code 1 - alpha < -1}; or {@code 1 - alpha >= 1};
  1325.      * or {@code beta < -1}; or {@code beta > 1}.
  1326.      */
  1327.     private static void validateParameters(double alpha, double beta) {
  1328.         // The epsilon (1-alpha) value must be in the interval [-1, 1).
  1329.         // Logic inversion will identify NaN
  1330.         final double eps = 1 - alpha;
  1331.         if (!(-1 <= eps && eps < 1)) {
  1332.             throw new IllegalArgumentException("alpha is not in the interval (0, 2]: " + alpha);
  1333.         }
  1334.         if (!(-1 <= beta && beta <= 1)) {
  1335.             throw new IllegalArgumentException("beta is not in the interval [-1, 1]: " + beta);
  1336.         }
  1337.     }

  1338.     /**
  1339.      * Validate the parameters are in the correct range.
  1340.      *
  1341.      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
  1342.      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
  1343.      * @param gamma Scale parameter. Must be strictly positive and finite.
  1344.      * @param delta Location parameter. Must be finite.
  1345.      * @throws IllegalArgumentException if {@code 1 - alpha < -1}; or {@code 1 - alpha >= 1};
  1346.      * or {@code beta < -1}; or {@code beta > 1}; or {@code gamma <= 0}; or
  1347.      * {@code gamma} or {@code delta} are not finite.
  1348.      */
  1349.     private static void validateParameters(double alpha, double beta,
  1350.                                            double gamma, double delta) {
  1351.         validateParameters(alpha, beta);

  1352.         InternalUtils.requireStrictlyPositiveFinite(gamma, "gamma");
  1353.         InternalUtils.requireFinite(delta, "delta");
  1354.     }
  1355. }