From 570d87782148e19489bd03e9a13ad26341f7188a Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Fri, 17 Jul 2020 01:19:40 +0200 Subject: [PATCH 01/11] Implement Cheng algorithm for sampling `Beta` This should be faster than the gamma variate transformation we are currently using, and it seems to work better for parameters smaller than one. The algorithm is also used by the R language, however I did not consult their implementation in order to avoid licensing problems. Reference: R. C. H. Cheng (1978). Generating beta variates with nonintegral shape parameters. Communications of the ACM 21, 317-322. https://doi.org/10.1145/359460.359482 --- rand_distr/CHANGELOG.md | 1 + rand_distr/benches/distributions.rs | 4 + rand_distr/src/gamma.rs | 179 +++++++++++++++++++++++++--- rand_distr/tests/value_stability.rs | 32 +++-- 4 files changed, 188 insertions(+), 28 deletions(-) diff --git a/rand_distr/CHANGELOG.md b/rand_distr/CHANGELOG.md index 99bfc66d7fc..e484f1b772e 100644 --- a/rand_distr/CHANGELOG.md +++ b/rand_distr/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - All error types now implement `std::error::Error` (#919) - Re-exported `rand::distributions::BernoulliError` (#919) - Add value stability tests for distributions (#891) +- Improve algorithm for sampling `Beta` (#1000) ## [0.2.2] - 2019-09-10 - Fix version requirement on rand lib (#847) diff --git a/rand_distr/benches/distributions.rs b/rand_distr/benches/distributions.rs index b0cd96dc6ab..4f9b35e0bc0 100644 --- a/rand_distr/benches/distributions.rs +++ b/rand_distr/benches/distributions.rs @@ -112,6 +112,10 @@ distr_float!(distr_normal, f64, Normal::new(-1.23, 4.56).unwrap()); distr_float!(distr_log_normal, f64, LogNormal::new(-1.23, 4.56).unwrap()); distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0).unwrap()); distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0).unwrap()); +distr_float!(distr_beta_small_param, f64, Beta::new(0.1, 0.1).unwrap()); +distr_float!(distr_beta_large_param_similar, f64, Beta::new(101., 95.).unwrap()); +distr_float!(distr_beta_large_param_different, f64, Beta::new(10., 1000.).unwrap()); +distr_float!(distr_beta_mixed_param, f64, Beta::new(0.5, 100.).unwrap()); distr_float!(distr_cauchy, f64, Cauchy::new(4.2, 6.9).unwrap()); distr_float!(distr_triangular, f64, Triangular::new(0., 1., 0.9).unwrap()); distr_int!(distr_binomial, u64, Binomial::new(20, 0.7).unwrap()); diff --git a/rand_distr/src/gamma.rs b/rand_distr/src/gamma.rs index 34cb45dfb36..c1f06e1aa07 100644 --- a/rand_distr/src/gamma.rs +++ b/rand_distr/src/gamma.rs @@ -495,6 +495,38 @@ where } } +/// The algorithm used for sampling the Beta distribution. +/// +/// Reference: +/// +/// R. C. H. Cheng (1978). +/// Generating beta variates with nonintegral shape parameters. +/// Communications of the ACM 21, 317-322. +/// https://doi.org/10.1145/359460.359482 +#[derive(Clone, Copy, Debug)] +enum BetaAlgorithm { + BB(BB), + BC(BC), +} + +/// Algorithm BB for `min(alpha, beta) > 1`. +#[derive(Clone, Copy, Debug)] +struct BB { + alpha: N, + beta: N, + gamma: N, +} + +/// Algorithm BC for `min(alpha, beta) <= 1`. +#[derive(Clone, Copy, Debug)] +struct BC { + alpha: N, + beta: N, + delta: N, + kappa1: N, + kappa2: N, +} + /// The Beta distribution with shape parameters `alpha` and `beta`. /// /// # Example @@ -510,12 +542,11 @@ where pub struct Beta where F: Float, - StandardNormal: Distribution, - Exp1: Distribution, Open01: Distribution, { - gamma_a: Gamma, - gamma_b: Gamma, + a: F, a0: F, + b: F, b0: F, + algorithm: BetaAlgorithm, } /// Error type returned from `Beta::new`. @@ -542,31 +573,140 @@ impl std::error::Error for BetaError {} impl Beta where F: Float, - StandardNormal: Distribution, - Exp1: Distribution, Open01: Distribution, { /// Construct an object representing the `Beta(alpha, beta)` /// distribution. pub fn new(alpha: F, beta: F) -> Result, BetaError> { - Ok(Beta { - gamma_a: Gamma::new(alpha, F::one()).map_err(|_| BetaError::AlphaTooSmall)?, - gamma_b: Gamma::new(beta, F::one()).map_err(|_| BetaError::BetaTooSmall)?, - }) + if !(alpha > F::zero()) { + return Err(BetaError::AlphaTooSmall); + } + if !(beta > F::zero()) { + return Err(BetaError::BetaTooSmall); + } + // From now on, we use the notation from the reference, + // i.e. `alpha` and `beta` are renamed to `a0` and `b0`. + let (a0, b0) = (alpha, beta); + let (a, b) = if a0 < b0 { (a0, b0) } else { (b0, a0) }; + if alpha > F::one() { + let alpha = a + b; + let beta = ((alpha - F::from(2.).unwrap()) + / (F::from(2.).unwrap()*a*b - alpha)).sqrt(); + let gamma = a + F::one() / beta; + + Ok(Beta { + a, a0, b, b0, + algorithm: BetaAlgorithm::BB(BB { + alpha, beta, gamma, + }) + }) + } else { + let alpha = a + b; + let beta = F::one() / b; + let delta = F::one() + a - b; + let kappa1 = delta + * (F::from(0.0138889).unwrap() + F::from(0.0416667).unwrap()*b) + / (a*beta - F::from(0.777778).unwrap()); + let kappa2 = F::from(0.25).unwrap() + + (F::from(0.5).unwrap() + F::from(0.25).unwrap()/delta)*b; + + Ok(Beta { + a, a0, b, b0, + algorithm: BetaAlgorithm::BC(BC { + alpha, beta, delta, kappa1, kappa2, + }) + }) + } } } impl Distribution for Beta where F: Float, - StandardNormal: Distribution, - Exp1: Distribution, Open01: Distribution, { fn sample(&self, rng: &mut R) -> F { - let x = self.gamma_a.sample(rng); - let y = self.gamma_b.sample(rng); - x / (x + y) + match self.algorithm { + BetaAlgorithm::BB(algo) => { + let mut w; + loop { + // 1. + let u1 = rng.sample(Open01); + let u2 = rng.sample(Open01); + let v = algo.beta * (u1 / (F::one() - u1)).ln(); + w = self.a * v.exp(); + let z = u1*u1 * u2; + let r = algo.gamma * v - F::from(4.).unwrap().ln(); + let s = self.a + r - w; + // 2. + if s + F::one() + F::from(5.).unwrap().ln() + >= F::from(5.).unwrap() * z { + break; + } + // 3. + let t = z.ln(); + if s >= t { + break; + } + // 4. + if !(r + algo.alpha * (algo.alpha / (self.b + w)).ln() < t) { + break; + } + } + // 5. + if self.a == self.a0 { + w / (self.b + w) + } else { + self.b / (self.b + w) + } + }, + BetaAlgorithm::BC(algo) => { + let mut w; + loop { + let z; + // 1. + let u1 = rng.sample(Open01); + let u2 = rng.sample(Open01); + if u1 < F::from(0.5).unwrap() { + // 2. + let y = u1 * u2; + z = u1 * y; + if F::from(0.25).unwrap() * u2 + z - y >= algo.kappa1 { + continue; + } + } else { + // 3. + z = u1 * u1 * u2; + if z <= F::from(0.25).unwrap() { + let v = algo.beta * (u1 / (F::one() - u1)).ln(); + w = self.a * v.exp(); + break; + } + // 4. + if z >= algo.kappa2 { + continue; + } + } + // 5. + let v = algo.beta * (u1 / (F::one() - u1)).ln(); + w = self.a * v.exp(); + if !(algo.alpha * ((algo.alpha / (self.b + w)).ln() + v) + - F::from(1.3862944).unwrap() < z.ln()) { + break; + }; + } + // 6. + if self.a == self.a0 { + if w == F::infinity() { + // Assuming `b` is finite, for large `w`: + return F::one(); + } + w / (self.b + w) + } else { + self.b / (self.b + w) + } + }, + } } } @@ -636,4 +776,13 @@ mod test { fn test_beta_invalid_dof() { Beta::new(0., 0.).unwrap(); } + + #[test] + fn test_beta_small_param() { + let beta = Beta::::new(1e-3, 1e-3).unwrap(); + let mut rng = crate::test::rng(206); + for i in 0..1000 { + assert!(!beta.sample(&mut rng).is_nan(), "failed at i={}", i); + } + } } diff --git a/rand_distr/tests/value_stability.rs b/rand_distr/tests/value_stability.rs index 192ba748b7f..5d2d96b17cb 100644 --- a/rand_distr/tests/value_stability.rs +++ b/rand_distr/tests/value_stability.rs @@ -121,11 +121,11 @@ fn normal_inverse_gaussian_stability() { fn pert_stability() { // mean = 4, var = 12/7 test_samples(860, Pert::new(2., 10., 3.).unwrap(), &[ - 4.631484136029422f64, - 3.307201472321789f64, - 3.29995019556348f64, - 3.66835483991721f64, - 3.514246139933899f64, + 4.908681667460367, + 4.014196196158352, + 2.6489397149197234, + 3.4569780580044727, + 4.242864311947118, ]); } @@ -200,15 +200,21 @@ fn gamma_stability() { -2.377641221169782, ]); - // Beta has same special cases as Gamma on each param + // Beta has two special cases: + // + // 1. min(alpha, beta) <= 1 + // 2. min(alpha, beta > 1 test_samples(223, Beta::new(1.0, 0.8).unwrap(), &[ - 0.6444564f32, 0.357635, 0.4110078, 0.7347192, - ]); - test_samples(223, Beta::new(0.7, 1.2).unwrap(), &[ - 0.6433129944095513f64, - 0.5373371199711573, - 0.10313293199269491, - 0.002472280249144378, + 0.2958284085602274, + 0.9384411906056516, + 0.3151361582723264, + 0.6150273348630618, + ]); + test_samples(223, Beta::new(3.0, 1.2).unwrap(), &[ + 0.49563509121756827, + 0.9551305482256759, + 0.5151181353461637, + 0.7551732971235077, ]); } From bd4c59ede214197278650ac30dc313c27622ddfc Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Fri, 17 Jul 2020 17:16:27 +0200 Subject: [PATCH 02/11] Replace constants with more precise values --- rand_distr/src/gamma.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rand_distr/src/gamma.rs b/rand_distr/src/gamma.rs index c1f06e1aa07..69fcd166f44 100644 --- a/rand_distr/src/gamma.rs +++ b/rand_distr/src/gamma.rs @@ -605,8 +605,8 @@ where let beta = F::one() / b; let delta = F::one() + a - b; let kappa1 = delta - * (F::from(0.0138889).unwrap() + F::from(0.0416667).unwrap()*b) - / (a*beta - F::from(0.777778).unwrap()); + * (F::from(1. / 18. / 4.).unwrap() + F::from(3. / 18. / 4.).unwrap()*b) + / (a*beta - F::from(14. / 18.).unwrap()); let kappa2 = F::from(0.25).unwrap() + (F::from(0.5).unwrap() + F::from(0.25).unwrap()/delta)*b; @@ -691,7 +691,7 @@ where let v = algo.beta * (u1 / (F::one() - u1)).ln(); w = self.a * v.exp(); if !(algo.alpha * ((algo.alpha / (self.b + w)).ln() + v) - - F::from(1.3862944).unwrap() < z.ln()) { + - F::from(4.).unwrap().ln() < z.ln()) { break; }; } From 296dabda4ee01a3c98a0451ae9d5b2572545f763 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Fri, 17 Jul 2020 17:55:22 +0200 Subject: [PATCH 03/11] Optimize struct size of `Beta` --- rand_distr/src/gamma.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/rand_distr/src/gamma.rs b/rand_distr/src/gamma.rs index 69fcd166f44..6ac76554d27 100644 --- a/rand_distr/src/gamma.rs +++ b/rand_distr/src/gamma.rs @@ -544,8 +544,7 @@ where F: Float, Open01: Distribution, { - a: F, a0: F, - b: F, b0: F, + a: F, b: F, switched_params: bool, algorithm: BetaAlgorithm, } @@ -587,7 +586,11 @@ where // From now on, we use the notation from the reference, // i.e. `alpha` and `beta` are renamed to `a0` and `b0`. let (a0, b0) = (alpha, beta); - let (a, b) = if a0 < b0 { (a0, b0) } else { (b0, a0) }; + let (a, b, switched_params) = if a0 < b0 { + (a0, b0, false) + } else { + (b0, a0, true) + }; if alpha > F::one() { let alpha = a + b; let beta = ((alpha - F::from(2.).unwrap()) @@ -595,7 +598,7 @@ where let gamma = a + F::one() / beta; Ok(Beta { - a, a0, b, b0, + a, b, switched_params, algorithm: BetaAlgorithm::BB(BB { alpha, beta, gamma, }) @@ -611,7 +614,7 @@ where + (F::from(0.5).unwrap() + F::from(0.25).unwrap()/delta)*b; Ok(Beta { - a, a0, b, b0, + a, b, switched_params, algorithm: BetaAlgorithm::BC(BC { alpha, beta, delta, kappa1, kappa2, }) @@ -654,7 +657,7 @@ where } } // 5. - if self.a == self.a0 { + if !self.switched_params { w / (self.b + w) } else { self.b / (self.b + w) @@ -696,7 +699,7 @@ where }; } // 6. - if self.a == self.a0 { + if !self.switched_params { if w == F::infinity() { // Assuming `b` is finite, for large `w`: return F::one(); From b6b241bd71db534fdd6ad701994ccf25e29fde7c Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Tue, 4 Aug 2020 19:56:31 +0200 Subject: [PATCH 04/11] rand_distr: Fix benchmarks --- rand_distr/benches/distributions.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rand_distr/benches/distributions.rs b/rand_distr/benches/distributions.rs index 4f9b35e0bc0..6776901d224 100644 --- a/rand_distr/benches/distributions.rs +++ b/rand_distr/benches/distributions.rs @@ -20,7 +20,7 @@ use std::mem::size_of; use test::Bencher; use rand::prelude::*; -use rand_distr::{weighted::WeightedIndex, *}; +use rand_distr::*; // At this time, distributions are optimised for 64-bit platforms. use rand_pcg::Pcg64Mcg; @@ -120,7 +120,7 @@ distr_float!(distr_cauchy, f64, Cauchy::new(4.2, 6.9).unwrap()); distr_float!(distr_triangular, f64, Triangular::new(0., 1., 0.9).unwrap()); distr_int!(distr_binomial, u64, Binomial::new(20, 0.7).unwrap()); distr_int!(distr_binomial_small, u64, Binomial::new(1000000, 1e-30).unwrap()); -distr_int!(distr_poisson, u64, Poisson::new(4.0).unwrap()); +distr_float!(distr_poisson, f64, Poisson::new(4.0).unwrap()); distr!(distr_bernoulli, bool, Bernoulli::new(0.18).unwrap()); distr_arr!(distr_circle, [f64; 2], UnitCircle); distr_arr!(distr_sphere, [f64; 3], UnitSphere); @@ -131,10 +131,10 @@ distr_int!(distr_weighted_u32, usize, WeightedIndex::new(&[1u32, 2, 3, 4, 12, 0, distr_int!(distr_weighted_f64, usize, WeightedIndex::new(&[1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap()); distr_int!(distr_weighted_large_set, usize, WeightedIndex::new((0..10000).rev().chain(1..10001)).unwrap()); -distr_int!(distr_weighted_alias_method_i8, usize, weighted::alias_method::WeightedIndex::new(vec![1i8, 2, 3, 4, 12, 0, 2, 1]).unwrap()); -distr_int!(distr_weighted_alias_method_u32, usize, weighted::alias_method::WeightedIndex::new(vec![1u32, 2, 3, 4, 12, 0, 2, 1]).unwrap()); -distr_int!(distr_weighted_alias_method_f64, usize, weighted::alias_method::WeightedIndex::new(vec![1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap()); -distr_int!(distr_weighted_alias_method_large_set, usize, weighted::alias_method::WeightedIndex::new((0..10000).rev().chain(1..10001).collect()).unwrap()); +distr_int!(distr_weighted_alias_method_i8, usize, WeightedAliasIndex::new(vec![1i8, 2, 3, 4, 12, 0, 2, 1]).unwrap()); +distr_int!(distr_weighted_alias_method_u32, usize, WeightedAliasIndex::new(vec![1u32, 2, 3, 4, 12, 0, 2, 1]).unwrap()); +distr_int!(distr_weighted_alias_method_f64, usize, WeightedAliasIndex::new(vec![1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap()); +distr_int!(distr_weighted_alias_method_large_set, usize, WeightedAliasIndex::new((0..10000).rev().chain(1..10001).collect()).unwrap()); #[bench] From e114a139f5f99f72e64e9eb6ba4c219aa5ab5a97 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Tue, 4 Aug 2020 19:59:33 +0200 Subject: [PATCH 05/11] Add rand_distr benches to CI --- utils/ci/script.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/ci/script.sh b/utils/ci/script.sh index efefc2adc5e..641ffb70ac4 100644 --- a/utils/ci/script.sh +++ b/utils/ci/script.sh @@ -29,6 +29,7 @@ main() { if [ "0$NIGHTLY" -ge 1 ]; then $CARGO test $TARGET --all-features $CARGO test $TARGET --benches --features=nightly + $CARGO test $TARGET --manifest-path rand_distr/Cargo.toml --benches --features=nightly else # all stable features: $CARGO test $TARGET --features=serde1,log,small_rng From bf12368ec0cfb7097f71c1711bedca472dace661 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Wed, 5 Aug 2020 09:10:15 +0200 Subject: [PATCH 06/11] Fix CI script --- utils/ci/script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/ci/script.sh b/utils/ci/script.sh index 641ffb70ac4..caef0767ca9 100644 --- a/utils/ci/script.sh +++ b/utils/ci/script.sh @@ -29,7 +29,7 @@ main() { if [ "0$NIGHTLY" -ge 1 ]; then $CARGO test $TARGET --all-features $CARGO test $TARGET --benches --features=nightly - $CARGO test $TARGET --manifest-path rand_distr/Cargo.toml --benches --features=nightly + $CARGO test $TARGET --manifest-path rand_distr/Cargo.toml --benches else # all stable features: $CARGO test $TARGET --features=serde1,log,small_rng From 90eca16d53249bc6132465f489179a030241dccb Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Mon, 7 Sep 2020 00:50:03 +0200 Subject: [PATCH 07/11] Fix Beta sampling algorithm --- rand_distr/src/gamma.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rand_distr/src/gamma.rs b/rand_distr/src/gamma.rs index 6ac76554d27..a5102fa75c5 100644 --- a/rand_distr/src/gamma.rs +++ b/rand_distr/src/gamma.rs @@ -591,7 +591,8 @@ where } else { (b0, a0, true) }; - if alpha > F::one() { + if a > F::one() { + // Algorithm BB let alpha = a + b; let beta = ((alpha - F::from(2.).unwrap()) / (F::from(2.).unwrap()*a*b - alpha)).sqrt(); @@ -604,6 +605,10 @@ where }) }) } else { + // Algorithm BC + // + // Here `a` is the maximum instead of the minimum. + let (a, b, switched_params) = (b, a, !switched_params); let alpha = a + b; let beta = F::one() / b; let delta = F::one() + a - b; @@ -658,6 +663,10 @@ where } // 5. if !self.switched_params { + if w == F::infinity() { + // Assuming `b` is finite, for large `w`: + return F::one(); + } w / (self.b + w) } else { self.b / (self.b + w) From 1320262696c7f9e9158f4bbfa82a926d97993742 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Mon, 7 Sep 2020 00:54:48 +0200 Subject: [PATCH 08/11] Reduce code duplication --- rand_distr/src/gamma.rs | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/rand_distr/src/gamma.rs b/rand_distr/src/gamma.rs index a5102fa75c5..5e98dbdfcfc 100644 --- a/rand_distr/src/gamma.rs +++ b/rand_distr/src/gamma.rs @@ -634,9 +634,9 @@ where Open01: Distribution, { fn sample(&self, rng: &mut R) -> F { + let mut w; match self.algorithm { BetaAlgorithm::BB(algo) => { - let mut w; loop { // 1. let u1 = rng.sample(Open01); @@ -661,19 +661,8 @@ where break; } } - // 5. - if !self.switched_params { - if w == F::infinity() { - // Assuming `b` is finite, for large `w`: - return F::one(); - } - w / (self.b + w) - } else { - self.b / (self.b + w) - } }, BetaAlgorithm::BC(algo) => { - let mut w; loop { let z; // 1. @@ -707,17 +696,17 @@ where break; }; } - // 6. - if !self.switched_params { - if w == F::infinity() { - // Assuming `b` is finite, for large `w`: - return F::one(); - } - w / (self.b + w) - } else { - self.b / (self.b + w) - } }, + }; + // 5. for BB, 6. for BC + if !self.switched_params { + if w == F::infinity() { + // Assuming `b` is finite, for large `w`: + return F::one(); + } + w / (self.b + w) + } else { + self.b / (self.b + w) } } } From c080f193b26f152dbfd4a017ccba8876f5a95ec8 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Mon, 7 Sep 2020 01:00:12 +0200 Subject: [PATCH 09/11] Fix value stability tests --- rand_distr/tests/value_stability.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rand_distr/tests/value_stability.rs b/rand_distr/tests/value_stability.rs index 5d2d96b17cb..7d7316096ca 100644 --- a/rand_distr/tests/value_stability.rs +++ b/rand_distr/tests/value_stability.rs @@ -203,12 +203,12 @@ fn gamma_stability() { // Beta has two special cases: // // 1. min(alpha, beta) <= 1 - // 2. min(alpha, beta > 1 + // 2. min(alpha, beta) > 1 test_samples(223, Beta::new(1.0, 0.8).unwrap(), &[ - 0.2958284085602274, - 0.9384411906056516, - 0.3151361582723264, - 0.6150273348630618, + 0.8300703726659456, + 0.8134131062097899, + 0.47912589330631555, + 0.25323238071138526, ]); test_samples(223, Beta::new(3.0, 1.2).unwrap(), &[ 0.49563509121756827, From c85e736b5dfdb51c941ff1a8e4e2a2aab67e29ef Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Mon, 7 Sep 2020 01:10:07 +0200 Subject: [PATCH 10/11] Fix changelog --- rand_distr/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rand_distr/CHANGELOG.md b/rand_distr/CHANGELOG.md index e484f1b772e..ef3e1fa2ae3 100644 --- a/rand_distr/CHANGELOG.md +++ b/rand_distr/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +- Improve algorithm for sampling `Beta` (#1000) + ## [0.3.0] - 2020-08-25 - Move alias method for `WeightedIndex` from `rand` (#945) - Rename `WeightedIndex` to `WeightedAliasIndex` (#1008) @@ -18,7 +21,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - All error types now implement `std::error::Error` (#919) - Re-exported `rand::distributions::BernoulliError` (#919) - Add value stability tests for distributions (#891) -- Improve algorithm for sampling `Beta` (#1000) ## [0.2.2] - 2019-09-10 - Fix version requirement on rand lib (#847) From 769e5d18fae9aa66efb354ea7ea6207092912043 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Mon, 7 Sep 2020 17:54:19 +0200 Subject: [PATCH 11/11] More specific changelog --- rand_distr/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rand_distr/CHANGELOG.md b/rand_distr/CHANGELOG.md index ef3e1fa2ae3..bd85462c2de 100644 --- a/rand_distr/CHANGELOG.md +++ b/rand_distr/CHANGELOG.md @@ -5,7 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -- Improve algorithm for sampling `Beta` (#1000) +- New `Beta` sampling algorithm for improved performance and accuracy (#1000) ## [0.3.0] - 2020-08-25 - Move alias method for `WeightedIndex` from `rand` (#945)