Skip to content

Improved PB Chance Module #642

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

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5bfc5b8
Create probability_distribution.rs
atw1020 Feb 6, 2023
b315b6f
Got code to compile and fixed syntax errors
atw1020 Feb 6, 2023
2841795
Completed Test of dirac delta function
atw1020 Feb 7, 2023
0191bb3
Finished first draft of unit step function, and expanded doccumentation
atw1020 Feb 7, 2023
01641b8
Reformatted project
atw1020 Feb 7, 2023
58589b2
Got unit step function to work
atw1020 Feb 7, 2023
dc3bab8
Implemented constructor for probability_distribution
atw1020 Feb 8, 2023
29451a1
Implemented probability_below function
atw1020 Feb 8, 2023
cd723ef
Doccumentation
atw1020 Feb 15, 2023
1224243
Merge branch 'LiveSplit:master' into master
atw1020 Feb 23, 2023
0521f43
Started investigating incorrect integrals
atw1020 Feb 23, 2023
1111ca6
Merge remote-tracking branch 'origin/master'
atw1020 Feb 23, 2023
f887282
Got probability less than function to work
atw1020 Feb 24, 2023
efba8ad
Got probability_below() function to work
atw1020 Feb 24, 2023
b3d0af3
Expanded documentation
atw1020 Feb 24, 2023
6e9be87
Implemented plotting and allowed ProbabilityDistribution::new() for s…
atw1020 Feb 24, 2023
37fb308
Added test for plotting function and fixed a bug I discovered with it
atw1020 Feb 24, 2023
998ec4a
Create benchmark for probability_distribution.rs
atw1020 Feb 24, 2023
e356cc0
Created test for adding distributions
atw1020 Feb 26, 2023
129ef90
Removed doc tests from probability_distribution.rs and added an add a…
atw1020 Feb 26, 2023
1eee3b7
Got Probability Distribution addition test working
atw1020 Feb 28, 2023
219b82b
Added black box to ensure conformity
atw1020 Feb 28, 2023
e539630
Merge branch 'LiveSplit:master' into master
atw1020 Mar 1, 2023
e45f60d
Add debug to ProbabilityDistribution
atw1020 Mar 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ serde_json = { version = "1.0.60", default-features = false, features = ["alloc"
smallstr = { version = "0.3.0", default-features = false }
snafu = { version = "0.7.0", default-features = false }
unicase = "2.6.0"
rustfft = "6.1.0"
assert_approx_eq = "1.1.0"

# std
image = { version = "0.24.0", features = [
Expand Down Expand Up @@ -180,6 +182,10 @@ harness = false
name = "software_rendering"
harness = false

[[bench]]
name = "statistical_pb_chance"
harness = false

[profile.max-opt]
inherits = "release"
lto = true
Expand Down
47 changes: 47 additions & 0 deletions benches/statistical_pb_chance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use criterion::{criterion_group, criterion_main, Criterion, black_box};
use livesplit_core::{RealTime, SegmentHistory, Time, TimeSpan, TimingMethod};
use livesplit_core::analysis::statistical_pb_chance::probability_distribution::ProbabilityDistribution;


criterion_main!(benches);
criterion_group!(benches, compute_probability_8192, add_distributions);

///
/// Computes a CDF of a probability distribution with 8192 data points
///
fn compute_probability_8192(c: &mut Criterion){

// initialize the distribution
let mut times = SegmentHistory::default();

times.insert(1, Time::from(RealTime(Some(TimeSpan::from_seconds(1.2)))));
times.insert(2, Time::from(RealTime(Some(TimeSpan::from_seconds(6.0)))));

let dist = ProbabilityDistribution::new(&times, TimingMethod::RealTime,
10.0, 8192, 0.5);

c.bench_function("Probability Less than x (8192 points)", move |b| {
b.iter(|| dist.probability_below(black_box(5.0)))
});

}

///
/// benchmarks adding two distributions together
///
fn add_distributions(c: &mut Criterion){
// initialize the distribution
let mut times = SegmentHistory::default();

times.insert(1, Time::from(RealTime(Some(TimeSpan::from_seconds(1.2)))));
times.insert(2, Time::from(RealTime(Some(TimeSpan::from_seconds(6.0)))));

let dist = ProbabilityDistribution::new(&times, TimingMethod::RealTime,
10.0, 8192, 0.5);

let other = dist.clone();

c.bench_function("Adding Distributions", |b| {
b.iter(|| &black_box(dist.clone()) + &other)
});
}
1 change: 1 addition & 0 deletions src/analysis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod skill_curve;
pub mod state_helper;
pub mod sum_of_segments;
pub mod total_playtime;
pub mod statistical_pb_chance;

pub use self::skill_curve::SkillCurve;
pub use self::state_helper::*;
Expand Down
127 changes: 127 additions & 0 deletions src/analysis/statistical_pb_chance/discontinuous_fourier_transforms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use rustfft::num_complex::Complex;

///
/// Functions that calculate the Fourier transforms of crucial discontinuous functions (Dirac Delta
/// and unit step functions)
///

///
/// computes the discrete fourier transform of a dirac delta function
///
/// # Arguments
///
/// * `omega_naught` - The fundamental frequency of the fourier transform we are computing
/// * `num_terms` - The number of terms in the fourier transform to return
/// * `time` - The point in time at which the delta function is nonzero
///
/// # Mathematics
///
/// The fourier transform of a delta function is given by:
///
/// ```latex
/// X[k] = e^{-t_{0}ωi}
/// ```
///
/// Where k is the index of the discrete fourier transform, t0 is the time at which the delta function
/// is nonzero, ω is the fundamental frequency (ω = k * ω0) and i is the complex unit (√(-1))
///
///
pub fn delta_function_dft(omega_naught: f32, num_terms: usize, time: f32) -> Vec<Complex<f32>> {

// determine the halfway point of the array. This is important because the frequencies in the second
// half of the transform are effectively negative frequencies. Since the formula for the fourier transform
// uses the frequency outside of a complex exponential we need to use the negative
// index of the second half of the array.
let cutoff = (num_terms as f32 / 2.0).ceil() as usize;

// initialize the fourier series
let mut fourier_series= vec![Complex::<f32>{re: 0.0, im: 0.0}; num_terms];

for k in 0..cutoff {
let omega = omega_naught * k as f32;
// println!("Omega: {}", omega);
fourier_series[k] = Complex::<f32>{re: 0.0, im: - omega * time}.exp();
}

for k in cutoff..num_terms {
let omega = omega_naught * (k as i64 - num_terms as i64) as f32; // different calculation of omega
// println!("Omega: {}", omega);
fourier_series[k] = Complex::<f32>{re: 0.0, im: - omega * time}.exp();
}

return fourier_series;
}

///
/// computes the discrete fourier transform of a unit step function. In particular, this function
/// returns a step down at the specified point
///
/// # Arguments
///
/// * `omega_naught` - the fundamental frequency of the fourier transform we are computing
/// * `num_terms` - the number of terms in the fourier transform to return
/// * `time` - the point in time at which the unit step function drops
///
/// # Mathematics
///
/// The fourier transform of a unit step function is given by:
///
/// ```latex
/// x[t] = u[t-t0] <=> X[k] = (𝛅[k] + 1 / (iω)) * e^{-iωt_{0}}
/// ```
///
/// Where k is the index of the discrete fourier transform, t0 is the point in time at which the
/// unit step function goes high, ω is the fundamental frequency (ω = k * ω0) and i is the complex unit (√(-1))
///
/// Unlike the dirac delta function, when we consider the unit step function, we must note that due to
/// the cyclic nature of the time domain, our step function must contain both a step up and a step down.
/// The definition of the unit step function seen above contains only a step up. We must therefore
/// add two step functions using the above definitions to obtain a true step function
///
/// The unit step function returned by this function is initially 1, then steps down to zero at the point `time`
/// The step function must therefore step up between the last element of the array and the first element. i.e.
/// the index -0.5. For this reason. Thus, the fourier transform returned by this function yields:
///
/// ```latex
/// u[t+0.5] - u[t-t_{0}]
/// ```
///
/// From the linearity of the Fourier transform and the distributive property, we get
///
/// ```latex
/// u[t+0.5] - u[t-t_{0}] <=> X[k] = (𝛅[k] + 1 / (iω)) * e^{-iω(-1/2)} - (𝛅[k] + 1 / (iω)) * e^{-iωt_{0}}
/// X[k] = (𝛅[k] + 1 / (iω)) * (e^{-iω(-1/2)} - e^{-iωt_{0}})
/// ```
///
pub fn step_function_dft(omega_naught: f32, num_terms: usize, time: f32) -> Vec<Complex<f32>> {
let mut fourier_series = vec![Complex::<f32>{re: 0.0, im: 0.0}; num_terms];

// determine the halfway point of the array. This is important because the frequencies in the second
// half of the transform are effectively negative frequencies. Since the formula for the fourier transform
// uses the frequency outside of a complex exponential (in particular, as 1/ω) we need to use the negative
// index of the second half of the array.
let cutoff = (num_terms as f32 / 2.0).ceil() as usize;

// 1/ω is undefined for ω=0, so we need to manually specify it
// X[0] is equal to the integral of the whole function. In our case, that's a unit step function
// that starts at -0.5 and ends at `time`. Therefore, the integral is time - (-0.5) = time + 0.5
fourier_series[0] = Complex::<f32>{re: (time + 0.5), im: 0.0};

for k in 1..cutoff {
let omega = omega_naught * k as f32;
// println!("Omega: {}", omega);
fourier_series[k] = Complex::<f32>{re: 0.0, im: - 1.0 / omega} * // 1/jω
(Complex::<f32>{re: 0.0, im: - omega * (-0.5)}.exp() - // e^{-iω(-1/2)}
Complex::<f32>{re: 0.0, im: - omega * (time)}.exp()); // e^{-iωt_{0}}
}

for k in cutoff..num_terms {
let omega = omega_naught * (k as i64 - num_terms as i64) as f32; // different calculation of omega
// println!("Omega: {}", omega);
fourier_series[k] = Complex::<f32>{re: 0.0, im: - 1.0 / omega} * // 1/jω
(Complex::<f32>{re: 0.0, im: - omega * (-0.5)}.exp() - // e^{-iω(-1/2)}
Complex::<f32>{re: 0.0, im: - omega * (time)}.exp()); // e^{-iωt_{0}}
}

return fourier_series;
}
7 changes: 7 additions & 0 deletions src/analysis/statistical_pb_chance/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! This module

pub mod probability_distribution;
pub mod discontinuous_fourier_transforms;

#[cfg(test)]
mod tests;
Loading