Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 13246200BB8 for ; Sat, 12 Nov 2016 20:53:22 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 11DA6160B14; Sat, 12 Nov 2016 19:53:22 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id EA9D0160B15 for ; Sat, 12 Nov 2016 20:53:20 +0100 (CET) Received: (qmail 27446 invoked by uid 500); 12 Nov 2016 19:53:14 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 25952 invoked by uid 99); 12 Nov 2016 19:53:13 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 12 Nov 2016 19:53:13 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 913DEF177A; Sat, 12 Nov 2016 19:53:13 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: erans@apache.org To: commits@commons.apache.org Date: Sat, 12 Nov 2016 19:53:30 -0000 Message-Id: In-Reply-To: <5cbe2821afea4b848092bcf6d86a61ee@git.apache.org> References: <5cbe2821afea4b848092bcf6d86a61ee@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [18/24] commons-rng git commit: RNG-30: Continuous distributions. archived-at: Sat, 12 Nov 2016 19:53:22 -0000 RNG-30: Continuous distributions. Code copied and adapted from classes in "o.a.c.math4.distribution" package. Project: http://git-wip-us.apache.org/repos/asf/commons-rng/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-rng/commit/3ece8fd4 Tree: http://git-wip-us.apache.org/repos/asf/commons-rng/tree/3ece8fd4 Diff: http://git-wip-us.apache.org/repos/asf/commons-rng/diff/3ece8fd4 Branch: refs/heads/RNG-30__sampling Commit: 3ece8fd401e0ed1a37db5267c32b3f7b5f43d219 Parents: 0996034 Author: Gilles Authored: Sat Nov 12 16:33:59 2016 +0100 Committer: Gilles Committed: Sat Nov 12 16:33:59 2016 +0100 ---------------------------------------------------------------------- .../AhrensDieterExponentialSampler.java | 117 +++++++++++++ .../AhrensDieterMarsagliaTsangGammaSampler.java | 139 +++++++++++++++ .../distribution/BoxMullerLogNormalSampler.java | 59 +++++++ .../sampling/distribution/ChengBetaSampler.java | 175 +++++++++++++++++++ .../distribution/ContinuousUniformSampler.java | 56 ++++++ 5 files changed, 546 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-rng/blob/3ece8fd4/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java ---------------------------------------------------------------------- diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java new file mode 100644 index 0000000..8807ce8 --- /dev/null +++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.rng.sampling.distribution; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.sampling.AbstractContinuousSampler; + +/** + * Sampling from an exponential distribution. + * + * @see Exponential distribution (MathWorld) + */ +public class AhrensDieterExponentialSampler extends AbstractContinuousSampler { + /** + * Table containing the constants + * \( q_i = sum_{j=1}^i (\ln 2)^j / j! = \ln 2 + (\ln 2)^2 / 2 + ... + (\ln 2)^i / i! \) + * until the largest representable fraction below 1 is exceeded. + * + * Note that + * \( 1 = 2 - 1 = \exp(\ln 2) - 1 = sum_{n=1}^\infinity (\ln 2)^n / n! \) + * thus \( q_i \rightarrow 1 as i \rightarrow +\infinity \), + * so the higher \( i \), the closer we get to 1 (the series is not alternating). + * + * By trying, n = 16 in Java is enough to reach 1. + */ + private static final double[] EXPONENTIAL_SA_QI = new double[16]; + /** The mean of this distribution. */ + private final double mean; + + /** + * Initialize tables. + */ + static { + /** + * Filling EXPONENTIAL_SA_QI table. + * Note that we don't want qi = 0 in the table. + */ + final double ln2 = Math.log(2); + double qi = 0; + + for (int i = 0; i < EXPONENTIAL_SA_QI.length; i++) { + qi += Math.pow(ln2, i + 1) / InternalUtils.factorial(i + 1); + EXPONENTIAL_SA_QI[i] = qi; + } + } + + /** + * @param rng Generator of uniformly distributed random numbers. + * @param mean Mean of this distribution. + */ + public AhrensDieterExponentialSampler(UniformRandomProvider rng, + double mean) { + super(rng); + this.mean = mean; + } + + /** {@inheritDoc} */ + @Override + public double sample() { + // Step 1: + double a = 0; + double u = nextUniform(); + + // Step 2 and 3: + while (u < 0.5) { + a += EXPONENTIAL_SA_QI[0]; + u *= 2; + } + + // Step 4 (now u >= 0.5): + u += u - 1; + + // Step 5: + if (u <= EXPONENTIAL_SA_QI[0]) { + return mean * (a + u); + } + + // Step 6: + int i = 0; // Should be 1, be we iterate before it in while using 0. + double u2 = nextUniform(); + double umin = u2; + + // Step 7 and 8: + do { + ++i; + u2 = nextUniform(); + + if (u2 < umin) { + umin = u2; + } + + // Step 8: + } while (u > EXPONENTIAL_SA_QI[i]); // Ensured to exit since EXPONENTIAL_SA_QI[MAX] = 1. + + return mean * (a + umin * EXPONENTIAL_SA_QI[0]); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "[Ahrens-Dieter method for exponential distribution " + super.toString() + "]"; + } +} http://git-wip-us.apache.org/repos/asf/commons-rng/blob/3ece8fd4/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java ---------------------------------------------------------------------- diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java new file mode 100644 index 0000000..14e6382 --- /dev/null +++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.rng.sampling.distribution; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.sampling.AbstractContinuousSampler; + +/** + *

+ * Sampling from the Gamma distribution. + *

    + *
  • + * For {@code 0 < shape < 1}: + *
    + * Ahrens, J. H. and Dieter, U., + * Computer methods for sampling from gamma, beta, Poisson and binomial distributions, + * Computing, 12, 223-246, 1974. + *
    + *
  • + *
  • + * For {@code shape >= 1}: + *
    + * Marsaglia and Tsang, A Simple Method for Generating + * Gamma Variables. ACM Transactions on Mathematical Software, + * Volume 26 Issue 3, September, 2000. + *
    + *
  • + *
+ *

+ */ +public class AhrensDieterMarsagliaTsangGammaSampler extends AbstractContinuousSampler { + /** The shape parameter. */ + private final double theta; + /** The alpha parameter. */ + private final double alpha; + /** Gaussian sampling. */ + private final BoxMullerGaussianSampler gaussian; + + /** + * @param rng Generator of uniformly distributed random numbers. + * @param alpha Alpha parameter of the distribution. + * @param theta Theta parameter of the distribution. + */ + public AhrensDieterMarsagliaTsangGammaSampler(UniformRandomProvider rng, + double alpha, + double theta) { + super(rng); + this.alpha = alpha; + this.theta = theta; + gaussian = new BoxMullerGaussianSampler(rng, 0, 1); + } + + /** {@inheritDoc} */ + @Override + public double sample() { + if (theta < 1) { + // [1]: p. 228, Algorithm GS. + + while (true) { + // Step 1: + final double u = nextUniform(); + final double bGS = 1 + theta / Math.E; + final double p = bGS * u; + + if (p <= 1) { + // Step 2: + + final double x = Math.pow(p, 1 / theta); + final double u2 = nextUniform(); + + if (u2 > Math.exp(-x)) { + // Reject. + continue; + } else { + return alpha * x; + } + } else { + // Step 3: + + final double x = -1 * Math.log((bGS - p) / theta); + final double u2 = nextUniform(); + + if (u2 > Math.pow(x, theta - 1)) { + // Reject. + continue; + } else { + return alpha * x; + } + } + } + } + + // Now theta >= 1. + + final double d = theta - 0.333333333333333333; + final double c = 1 / (3 * Math.sqrt(d)); + + while (true) { + final double x = gaussian.sample(); + final double v = (1 + c * x) * (1 + c * x) * (1 + c * x); + + if (v <= 0) { + continue; + } + + final double x2 = x * x; + final double u = nextUniform(); + + // Squeeze. + if (u < 1 - 0.0331 * x2 * x2) { + return alpha * d * v; + } + + if (Math.log(u) < 0.5 * x2 + d * (1 - v + Math.log(v))) { + return alpha * d * v; + } + } + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "[Ahrens-Dieter-Marsaglia-Tsang method for Gamma distribution " + super.toString() + "]"; + } +} http://git-wip-us.apache.org/repos/asf/commons-rng/blob/3ece8fd4/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java ---------------------------------------------------------------------- diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java new file mode 100644 index 0000000..4cda8d4 --- /dev/null +++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.rng.sampling.distribution; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.sampling.AbstractContinuousSampler; + +/** + * + * Box-Muller algorithm for sampling from a Log normal distribution. + */ +public class BoxMullerLogNormalSampler extends AbstractContinuousSampler { + /** Scale. */ + private final double scale; + /** Shape. */ + private final double shape; + /** Gaussian sampling. */ + private final BoxMullerGaussianSampler gaussian; + + /** + * @param rng Generator of uniformly distributed random numbers. + * @param scale Scale of the Log normal distribution. + * @param shape Shape of the Log normal distribution. + */ + public BoxMullerLogNormalSampler(UniformRandomProvider rng, + double scale, + double shape) { + super(null); // Not used. + this.scale = scale; + this.shape = shape; + gaussian = new BoxMullerGaussianSampler(rng, 0, 1); + } + + /** {@inheritDoc} */ + @Override + public double sample() { + return Math.exp(scale + shape * gaussian.sample()); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "[Box-Muller method for Log normal distribution " + gaussian.toString() + "]"; + } +} http://git-wip-us.apache.org/repos/asf/commons-rng/blob/3ece8fd4/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java ---------------------------------------------------------------------- diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java new file mode 100644 index 0000000..d0bfd41 --- /dev/null +++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.rng.sampling.distribution; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.sampling.AbstractContinuousSampler; + + +/** + * Utility class implementing Cheng's algorithms for beta distribution sampling. + * + *
+ *
+ * R. C. H. Cheng,
+ * "Generating beta variates with nonintegral shape parameters",
+ * Communications of the ACM, 21, 317-322, 1978.
+ * 
+ *
+ * + * @since 1.0 + */ +public class ChengBetaSampler extends AbstractContinuousSampler { + /** First shape parameter. */ + private final double alphaShape; + /** Second shape parameter. */ + private final double betaShape; + + /** + * Creates a sampler instance. + * + * @param rng Generator of uniformly distributed random numbers. + * @param alpha Distribution first shape parameter. + * @param beta Distribution second shape parameter. + */ + public ChengBetaSampler(UniformRandomProvider rng, + double alpha, + double beta) { + super(rng); + alphaShape = alpha; + betaShape = beta; + } + + /** {@inheritDoc} */ + @Override + public double sample() { + final double a = Math.min(alphaShape, betaShape); + final double b = Math.max(alphaShape, betaShape); + + if (a > 1) { + return algorithmBB(a, b); + } else { + return algorithmBC(b, a); + } + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "[Cheng method for Beta distribution " + super.toString() + "]"; + } + + /** + * Computes one sample using Cheng's BB algorithm, when \( \alpha \) and + * \( \beta \) are both larger than 1. + * + * @param a \( \min(\alpha, \beta) \). + * @param b \( \max(\alpha, \beta) \). + * @return a random sample. + */ + private double algorithmBB(double a, + double b) { + final double alpha = a + b; + final double beta = Math.sqrt((alpha - 2) / (2 * a * b - alpha)); + final double gamma = a + 1 / beta; + + double r; + double w; + double t; + do { + final double u1 = nextUniform(); + final double u2 = nextUniform(); + final double v = beta * (Math.log(u1) - Math.log1p(-u1)); + w = a * Math.exp(v); + final double z = u1 * u1 * u2; + r = gamma * v - 1.3862944; + final double s = a + r - w; + if (s + 2.609438 >= 5 * z) { + break; + } + + t = Math.log(z); + if (s >= t) { + break; + } + } while (r + alpha * (Math.log(alpha) - Math.log(b + w)) < t); + + w = Math.min(w, Double.MAX_VALUE); + + return equals(a, alphaShape) ? w / (b + w) : b / (b + w); + } + + /** + * Computes one sample using Cheng's BB algorithm, when at least one of + * \( \alpha \) or \( \beta \) is smaller than 1. + * + * @param a \( \min(\alpha, \beta) \). + * @param b \( \max(\alpha, \beta) \). + * @return a random sample. + */ + private double algorithmBC(double a, + double b) { + final double alpha = a + b; + final double beta = 1 / b; + final double delta = 1 + a - b; + final double k1 = delta * (0.0138889 + 0.0416667 * b) / (a * beta - 0.777778); + final double k2 = 0.25 + (0.5 + 0.25 / delta) * b; + + double w; + while (true) { + final double u1 = nextUniform(); + final double u2 = nextUniform(); + final double y = u1 * u2; + final double z = u1 * y; + if (u1 < 0.5) { + if (0.25 * u2 + z - y >= k1) { + continue; + } + } else { + if (z <= 0.25) { + final double v = beta * (Math.log(u1) - Math.log1p(-u1)); + w = a * Math.exp(v); + break; + } + + if (z >= k2) { + continue; + } + } + + final double v = beta * (Math.log(u1) - Math.log1p(-u1)); + w = a * Math.exp(v); + if (alpha * (Math.log(alpha) - Math.log(b + w) + v) - 1.3862944 >= Math.log(z)) { + break; + } + } + + w = Math.min(w, Double.MAX_VALUE); + + return equals(a, alphaShape) ? w / (b + w) : b / (b + w); + } + + /** + * @param a Value. + * @param b Value. + * @return {@code true} if {@code a} is equal to {@code b}. + */ + private boolean equals(double a, + double b) { + return Math.abs(a - b) <= Double.MIN_VALUE; + } +} http://git-wip-us.apache.org/repos/asf/commons-rng/blob/3ece8fd4/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java ---------------------------------------------------------------------- diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java new file mode 100644 index 0000000..a77cddc --- /dev/null +++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.rng.sampling.distribution; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.sampling.AbstractContinuousSampler; + +/** + * Sampling from a uniform distribution. + */ +public class ContinuousUniformSampler extends AbstractContinuousSampler { + /** Lower bound. */ + private final double lo; + /** Higher bound. */ + private final double hi; + + /** + * @param rng Generator of uniformly distributed random numbers. + * @param lo Lower bound. + * @param hi Higher bound. + */ + public ContinuousUniformSampler(UniformRandomProvider rng, + double lo, + double hi) { + super(rng); + this.lo = lo; + this.hi = hi; + } + + /** {@inheritDoc} */ + @Override + public double sample() { + final double u = nextUniform(); + return u * hi + (1 - u) * lo; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "[Uniform distribution " + super.toString() + "]"; + } +}