-
-
Notifications
You must be signed in to change notification settings - Fork 486
Compare sampled normal distribution to PDF #1121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
fd50cd4
Update dependency
vks 0eb2454
Implement sparklines
vks c8561f4
rand_distr: Test normal distribution
vks e8329a2
Fix tests for old Rust versions
vks da94e87
More fixes for old Rust versions
vks 70efd2e
Another fix for old Rust
vks 0cde3bf
rand_distr: Fix clippy warnings
vks 67f491a
rand_core: Fix clippy warnings
vks aee0044
rand_hc: Fix clippy warnings
vks 1b8eaf2
rand: Fix clippy warnings
vks ab6f3dd
Add missing copyright headers
vks 8dac1dc
Fix Rust 1.36 compatibility
vks 76a7305
Define constant for histogram size
vks 66da6e6
Simplify loop
vks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| // Copyright 2021 Developers of the Rand project. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | ||
| // option. This file may not be copied, modified, or distributed | ||
| // except according to those terms. | ||
|
|
||
| #![allow(clippy::float_cmp)] | ||
|
|
||
| use average::Histogram; | ||
| use rand::{Rng, SeedableRng}; | ||
| use rand_distr::Normal; | ||
|
|
||
| const HIST_LEN: usize = 100; | ||
| average::define_histogram!(hist, crate::HIST_LEN); | ||
| use hist::Histogram as Histogram100; | ||
|
|
||
| mod sparkline; | ||
|
|
||
| #[test] | ||
| fn normal() { | ||
| const N_SAMPLES: u64 = 1_000_000; | ||
| const MEAN: f64 = 2.; | ||
| const STD_DEV: f64 = 0.5; | ||
| const MIN_X: f64 = -1.; | ||
| const MAX_X: f64 = 5.; | ||
|
|
||
| let dist = Normal::new(MEAN, STD_DEV).unwrap(); | ||
| let mut hist = Histogram100::with_const_width(MIN_X,MAX_X); | ||
| let mut rng = rand::rngs::SmallRng::seed_from_u64(1); | ||
|
|
||
| for _ in 0..N_SAMPLES { | ||
| let _ = hist.add(rng.sample(dist)); // Ignore out-of-range values | ||
| } | ||
|
|
||
| println!("Sampled normal distribution:\n{}", | ||
| sparkline::render_u64_as_string(hist.bins())); | ||
|
|
||
| fn pdf(x: f64) -> f64 { | ||
| (-0.5 * ((x - MEAN) / STD_DEV).powi(2)).exp() / | ||
| (STD_DEV * (2. * core::f64::consts::PI).sqrt()) | ||
| } | ||
|
|
||
| let mut bin_centers = hist.centers(); | ||
| let mut expected = [0.; HIST_LEN]; | ||
| for e in &mut expected[..] { | ||
| *e = pdf(bin_centers.next().unwrap()); | ||
| } | ||
|
|
||
| println!("Expected normal distribution:\n{}", | ||
| sparkline::render_u64_as_string(hist.bins())); | ||
|
|
||
| let mut diff = [0.; HIST_LEN]; | ||
| for (i, n) in hist.normalized_bins().enumerate() { | ||
| let bin = (n as f64) / (N_SAMPLES as f64) ; | ||
| diff[i] = (bin - expected[i]).abs(); | ||
| } | ||
|
|
||
| println!("Difference:\n{}", | ||
| sparkline::render_f64_as_string(&diff[..])); | ||
| println!("max diff: {:?}", diff.iter().fold( | ||
| core::f64::NEG_INFINITY, |a, &b| a.max(b))); | ||
|
|
||
| // Check that the differences are significantly smaller than the expected error. | ||
| let mut expected_error = [0.; HIST_LEN]; | ||
| // Calculate error from histogram | ||
| for (err, var) in expected_error.iter_mut().zip(hist.variances()) { | ||
| *err = var.sqrt() / (N_SAMPLES as f64); | ||
| } | ||
| // Normalize error by bin width | ||
| for (err, width) in expected_error.iter_mut().zip(hist.widths()) { | ||
| *err /= width; | ||
| } | ||
| // TODO: Calculate error from distribution cutoff / normalization | ||
|
|
||
| println!("max expected_error: {:?}", expected_error.iter().fold( | ||
| core::f64::NEG_INFINITY, |a, &b| a.max(b))); | ||
| for (&d, &e) in diff.iter().zip(expected_error.iter()) { | ||
| // Difference larger than 3 standard deviations or cutoff | ||
| let tol = (3. * e).max(1e-4); | ||
| if d > tol { | ||
| panic!("Difference = {} * tol", d / tol); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| // Copyright 2021 Developers of the Rand project. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | ||
| // option. This file may not be copied, modified, or distributed | ||
| // except according to those terms. | ||
|
|
||
| /// Number of ticks. | ||
| const N: usize = 8; | ||
| /// Ticks used for the sparkline. | ||
| static TICKS: [char; N] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']; | ||
|
|
||
| /// Render a sparkline of `data` into `buffer`. | ||
| pub fn render_u64(data: &[u64], buffer: &mut String) { | ||
| match data.len() { | ||
| 0 => { | ||
| return; | ||
| }, | ||
| 1 => { | ||
| if data[0] == 0 { | ||
| buffer.push(TICKS[0]); | ||
| } else { | ||
| buffer.push(TICKS[N - 1]); | ||
| } | ||
| return; | ||
| }, | ||
| _ => {}, | ||
| } | ||
| let max = data.iter().max().unwrap(); | ||
| let min = data.iter().min().unwrap(); | ||
| let scale = ((N - 1) as f64) / ((max - min) as f64); | ||
| for i in data { | ||
| let tick = (((i - min) as f64) * scale) as usize; | ||
| buffer.push(TICKS[tick]); | ||
| } | ||
| } | ||
|
|
||
| /// Calculate the required capacity for the sparkline, given the length of the | ||
| /// input data. | ||
| pub fn required_capacity(len: usize) -> usize { | ||
| len * TICKS[0].len_utf8() | ||
| } | ||
|
|
||
| /// Render a sparkline of `data` into a newly allocated string. | ||
| pub fn render_u64_as_string(data: &[u64]) -> String { | ||
| let cap = required_capacity(data.len()); | ||
| let mut s = String::with_capacity(cap); | ||
| render_u64(data, &mut s); | ||
| debug_assert_eq!(s.capacity(), cap); | ||
| s | ||
| } | ||
|
|
||
| /// Render a sparkline of `data` into `buffer`. | ||
| pub fn render_f64(data: &[f64], buffer: &mut String) { | ||
| match data.len() { | ||
| 0 => { | ||
| return; | ||
| }, | ||
| 1 => { | ||
| if data[0] == 0. { | ||
| buffer.push(TICKS[0]); | ||
| } else { | ||
| buffer.push(TICKS[N - 1]); | ||
| } | ||
| return; | ||
| }, | ||
| _ => {}, | ||
| } | ||
| for x in data { | ||
| assert!(x.is_finite(), "can only render finite values"); | ||
| } | ||
| let max = data.iter().fold( | ||
| core::f64::NEG_INFINITY, |a, &b| a.max(b)); | ||
| let min = data.iter().fold( | ||
| core::f64::INFINITY, |a, &b| a.min(b)); | ||
| let scale = ((N - 1) as f64) / (max - min); | ||
| for x in data { | ||
| let tick = ((x - min) * scale) as usize; | ||
| buffer.push(TICKS[tick]); | ||
| } | ||
| } | ||
|
|
||
| /// Render a sparkline of `data` into a newly allocated string. | ||
| pub fn render_f64_as_string(data: &[f64]) -> String { | ||
| let cap = required_capacity(data.len()); | ||
| let mut s = String::with_capacity(cap); | ||
| render_f64(data, &mut s); | ||
| debug_assert_eq!(s.capacity(), cap); | ||
| s | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| #[test] | ||
| fn render_u64() { | ||
| let data = [2, 250, 670, 890, 2, 430, 11, 908, 123, 57]; | ||
| let mut s = String::with_capacity(super::required_capacity(data.len())); | ||
| super::render_u64(&data, &mut s); | ||
| println!("{}", s); | ||
| assert_eq!("▁▂▆▇▁▄▁█▁▁", &s); | ||
| } | ||
|
|
||
| #[test] | ||
| fn render_u64_as_string() { | ||
| let data = [2, 250, 670, 890, 2, 430, 11, 908, 123, 57]; | ||
| let s = super::render_u64_as_string(&data); | ||
| println!("{}", s); | ||
| assert_eq!("▁▂▆▇▁▄▁█▁▁", &s); | ||
| } | ||
|
|
||
| #[test] | ||
| fn render_f64() { | ||
| let data = [2., 250., 670., 890., 2., 430., 11., 908., 123., 57.]; | ||
| let mut s = String::with_capacity(super::required_capacity(data.len())); | ||
| super::render_f64(&data, &mut s); | ||
| println!("{}", s); | ||
| assert_eq!("▁▂▆▇▁▄▁█▁▁", &s); | ||
| } | ||
|
|
||
| #[test] | ||
| fn render_f64_as_string() { | ||
| let data = [2., 250., 670., 890., 2., 430., 11., 908., 123., 57.]; | ||
| let s = super::render_f64_as_string(&data); | ||
| println!("{}", s); | ||
| assert_eq!("▁▂▆▇▁▄▁█▁▁", &s); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.