protected double createSample()

in commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/StableSampler.java [626:689]


        protected double createSample(double phi, double w) {
            // Here we use the formula provided by Weron for the 1-parameterization.
            // Note: Adding back zeta creates the 0-parameterization defined in Nolan (1998):
            // X ~ S0_alpha(s,beta,u0) with s=1, u0=0 for a standard random variable.
            // As alpha -> 1 the translation zeta to create the stable deviate
            // in the 0-parameterization is increasingly large as tan(pi/2) -> infinity.
            // The max translation is approximately 1e16.
            // Without this translation the stable deviate is in the 1-parameterization
            // and the function is not continuous with respect to alpha.
            // Due to the large zeta when alpha -> 1 the number of bits of the output variable
            // are very low due to cancellation.

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

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

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

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

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

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

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