Skip to content

Commit 7460bbc

Browse files
committed
Ver 0.39.8
Implement LogNormal dist
2 parents c25baff + 8092adc commit 7460bbc

File tree

6 files changed

+97
-23
lines changed

6 files changed

+97
-23
lines changed

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "peroxide"
3-
version = "0.39.7"
3+
version = "0.39.8"
44
authors = ["axect <axect@outlook.kr>"]
55
edition = "2018"
66
description = "Rust comprehensive scientific computation library contains linear algebra, numerical analysis, statistics and machine learning tools with farmiliar syntax"
@@ -38,7 +38,6 @@ peroxide-ad = "0.3"
3838
peroxide-num = "0.1"
3939
anyhow = "1.0"
4040
paste = "1.0"
41-
#num-complex = "0.3"
4241
netcdf = { version = "0.7", optional = true, default-features = false }
4342
pyo3 = { version = "0.22", optional = true, features = [
4443
"auto-initialize",

README.md

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,32 @@ Rust numeric library contains linear algebra, numerical analysis, statistics and
1111

1212
## Table of Contents
1313

14-
- [Why Peroxide?](#why-peroxide)
15-
- [1. Customize features](#1-customize-features)
16-
- [2. Easy to optimize](#2-easy-to-optimize)
17-
- [3. Friendly syntax](#3-friendly-syntax)
18-
- [4. Can choose two different coding styles](#4-can-choose-two-different-coding-styles)
19-
- [5. Batteries included](#5-batteries-included)
20-
- [6. Compatible with Mathematics](#6-compatible-with-mathematics)
21-
- [7. Written in Rust](#7-written-in-rust)
22-
- [Latest README version](#latest-readme-version)
23-
- [Pre-requisite](#pre-requisite)
24-
- [Install](#install)
25-
- [Useful tips for features](#useful-tips-for-features)
26-
- [Module Structure](#module-structure)
27-
- [Documentation](#documentation)
28-
- [Examples](#examples)
29-
- [Release Info](#release-info)
30-
- [Contributes Guide](#contributes-guide)
31-
- [LICENSE](#license)
32-
- [TODO](#todo)
14+
- [Peroxide](#peroxide)
15+
- [Table of Contents](#table-of-contents)
16+
- [Why Peroxide?](#why-peroxide)
17+
- [1. Customize features](#1-customize-features)
18+
- [2. Easy to optimize](#2-easy-to-optimize)
19+
- [3. Friendly syntax](#3-friendly-syntax)
20+
- [4. Can choose two different coding styles.](#4-can-choose-two-different-coding-styles)
21+
- [5. Batteries included](#5-batteries-included)
22+
- [6. Compatible with Mathematics](#6-compatible-with-mathematics)
23+
- [7. Written in Rust](#7-written-in-rust)
24+
- [Latest README version](#latest-readme-version)
25+
- [Pre-requisite](#pre-requisite)
26+
- [Install](#install)
27+
- [Basic Installation](#basic-installation)
28+
- [Featured Installation](#featured-installation)
29+
- [Available Features](#available-features)
30+
- [Install Examples](#install-examples)
31+
- [Useful tips for features](#useful-tips-for-features)
32+
- [Module Structure](#module-structure)
33+
- [Documentation](#documentation)
34+
- [Examples](#examples)
35+
- [Release Info](#release-info)
36+
- [Contributes Guide](#contributes-guide)
37+
- [LICENSE](#license)
38+
- [TODO](#todo)
39+
- [Cite Peroxide](#cite-peroxide)
3340

3441
## Why Peroxide?
3542

@@ -223,6 +230,7 @@ Peroxide can do many things.
223230
- Beta
224231
- Student's-t
225232
- Weighted Uniform
233+
- LogNormal
226234
- RNG algorithms
227235
- Acceptance Rejection
228236
- Marsaglia Polar

RELEASES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Release 0.39.8 (2025-06-23)
2+
3+
- Implement `LogNormal` distribution
4+
- `LogNormal(mu: f64, sigma: f64)`
5+
- Fix sampling method for `Gamma`
6+
17
# Release 0.39.7 (2025-05-27)
28

39
- Add some methods for `DataFrame`

examples/dist.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,18 @@ fn example() {
6363
println!("PDF at x = mean: {}", g.pdf(0.666666));
6464
println!("PDF at x = mode: {}", g.pdf(0.5));
6565
println!("");
66+
67+
let m = 1.0;
68+
let s = 1.0;
69+
let lognorm = LogNormal(m, s);
70+
let lognorm_sample = lognorm.sample(SAMPLE_SIZE);
71+
println!("LogNormal at (m,s) = ({},{})", m, s);
72+
println!("Number of samples: {}", SAMPLE_SIZE);
73+
println!("Theoretical mean: {}", lognorm.mean());
74+
println!("Sample mean: {}", lognorm_sample.mean());
75+
println!("Theoretical var: {}", lognorm.var());
76+
println!("Sample var: {}", lognorm_sample.var());
77+
println!("PDF at x = mean: {}", lognorm.pdf(2.718281828459045));
78+
println!("PDF at x = mode: {}", lognorm.pdf(1.0));
79+
println!("");
6680
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
//! - Gamma
2828
//! - Beta
2929
//! - Student's t
30+
//! - LogNormal
3031
//! - [Special functions](special/function/index.html) (Using `puruspe` crate)
3132
//! - Gaussian
3233
//! - Gamma

src/statistics/dist.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//! * Student's t
1313
//! * Uniform
1414
//! * Weighted Uniform
15+
//! * Log Normal
1516
//! * There are two enums to represent probability distribution
1617
//! * `OPDist<T>` : One parameter distribution (Bernoulli)
1718
//! * `TPDist<T>` : Two parameter distribution (Uniform, Normal, Beta, Gamma)
@@ -226,6 +227,18 @@
226227
//!
227228
//! * Reference
228229
//! * [Piecewise Rejection Sampling](https://axect.github.io/posts/006_prs/#22-weighted-uniform-distribution)
230+
//!
231+
//! ### Log Normal Distribution
232+
//!
233+
//! * Definition
234+
//! $$\text{LogNormal}(x | \mu, \sigma) = \frac{1}{x\sigma\sqrt{2\pi}} e^{-\frac{(\ln x -
235+
//! \mu)^2}{2\sigma^2}}$$
236+
//! where $\mu$ is the mean of the natural logarithm of the variable and $\sigma$ is the
237+
//! standard deviation of the natural logarithm of the variable.
238+
//! * Representative value
239+
//! * Mean: $e^{\mu + \frac{\sigma^2}{2}}$
240+
//! * Var: $(e^{\sigma^2} - 1)e^{2\mu + \sigma^2}$
241+
//! * To generate log-normal random samples, Peroxide uses the `rand_distr::LogNormal` distribution from the `rand_distr` crate.
229242
230243
extern crate rand;
231244
extern crate rand_distr;
@@ -267,6 +280,7 @@ pub enum TPDist<T: PartialOrd + SampleUniform + Copy + Into<f64>> {
267280
Normal(T, T),
268281
Beta(T, T),
269282
Gamma(T, T),
283+
LogNormal(T, T),
270284
}
271285

272286
pub struct WeightedUniform<T: PartialOrd + SampleUniform + Copy + Into<f64>> {
@@ -312,7 +326,7 @@ impl WeightedUniform<f64> {
312326
///
313327
/// Ok(())
314328
/// }
315-
/// ```
329+
/// ```
316330
pub fn new(weights: Vec<f64>, intervals: Vec<f64>) -> Result<Self> {
317331
let mut weights = weights;
318332
if weights.len() == 0 {
@@ -497,6 +511,7 @@ impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> ParametricDist for TPDist
497511
Normal(mu, sigma) => ((*mu).into(), (*sigma).into()),
498512
Beta(a, b) => ((*a).into(), (*b).into()),
499513
Gamma(a, b) => ((*a).into(), (*b).into()),
514+
LogNormal(mu, sigma) => ((*mu).into(), (*sigma).into()),
500515
}
501516
}
502517
}
@@ -683,7 +698,7 @@ impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> RNG for TPDist<T> {
683698
// }
684699
Gamma(shape, scale) => {
685700
let gamma =
686-
rand_distr::Gamma::<f64>::new((*shape).into(), (*scale).into()).unwrap();
701+
rand_distr::Gamma::<f64>::new((*shape).into(), 1f64 / (*scale).into()).unwrap();
687702
gamma.sample_iter(rng).take(n).collect()
688703
} // Gamma(a, b) => {
689704
// let a_f64 = (*a).into();
@@ -711,6 +726,11 @@ impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> RNG for TPDist<T> {
711726
// }
712727
// v
713728
// }
729+
LogNormal(mu, sigma) => {
730+
let log_normal =
731+
rand_distr::LogNormal::<f64>::new((*mu).into(), (*sigma).into()).unwrap();
732+
log_normal.sample_iter(rng).take(n).collect()
733+
}
714734
}
715735
}
716736

@@ -753,6 +773,16 @@ impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> RNG for TPDist<T> {
753773
* x.into().powf(a_f64 - 1f64)
754774
* E.powf(-b_f64 * x.into())
755775
}
776+
LogNormal(mu, sigma) => {
777+
let mu_f64 = (*mu).into();
778+
let sigma_f64 = (*sigma).into();
779+
if x.into() <= 0f64 {
780+
0f64
781+
} else {
782+
1f64 / (x.into() * sigma_f64 * (2f64 * std::f64::consts::PI).sqrt())
783+
* E.powf(-((x.into().ln() - mu_f64).powi(2)) / (2f64 * sigma_f64.powi(2)))
784+
}
785+
}
756786
}
757787
}
758788

@@ -791,6 +821,11 @@ impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> RNG for TPDist<T> {
791821

792822
inc_gamma(a, b * x)
793823
}
824+
LogNormal(mu, sigma) => {
825+
phi(
826+
(x.ln() - (*mu).into()) / (*sigma).into(),
827+
)
828+
}
794829
}
795830
}
796831
}
@@ -886,6 +921,11 @@ impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> Statistics for TPDist<T>
886921
Normal(m, _s) => (*m).into(),
887922
Beta(a, b) => (*a).into() / ((*a).into() + (*b).into()),
888923
Gamma(a, b) => (*a).into() / (*b).into(),
924+
LogNormal(mu, sigma) => {
925+
let mu_f64 = (*mu).into();
926+
let sigma_f64 = (*sigma).into();
927+
E.powf(mu_f64 + 0.5 * sigma_f64.powi(2))
928+
}
889929
}
890930
}
891931

@@ -900,6 +940,11 @@ impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> Statistics for TPDist<T>
900940
a_f64 * b_f64 / ((a_f64 + b_f64).powi(2) * (a_f64 + b_f64 + 1f64))
901941
}
902942
Gamma(a, b) => (*a).into() / (*b).into().powi(2),
943+
LogNormal(mu, sigma) => {
944+
let mu_f64 = (*mu).into();
945+
let sigma_f64 = (*sigma).into();
946+
(E.powf(sigma_f64.powi(2)) - 1f64) * E.powf(2f64 * mu_f64 + sigma_f64.powi(2))
947+
}
903948
}
904949
}
905950

@@ -910,6 +955,7 @@ impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> Statistics for TPDist<T>
910955
Normal(_m, s) => (*s).into(),
911956
Beta(_a, _b) => self.var().sqrt(),
912957
Gamma(_a, _b) => self.var().sqrt(),
958+
LogNormal(_mu, _sigma) => self.var().sqrt(),
913959
}
914960
}
915961

0 commit comments

Comments
 (0)