diff --git a/src/error_analysis.rs b/src/error_analysis.rs index 7137f37..ee4fdd7 100644 --- a/src/error_analysis.rs +++ b/src/error_analysis.rs @@ -4,8 +4,10 @@ use super::perform_wham; use super::Config; use rgsl::statistics; +// returns a set of num_windows continious weights by +// a) generate num_windows-1 random variables and sort them +// b) each weight n is the difference between n+1 and n, where n0=0 and nN+1=1 fn generate_random_weights(num_windows: usize) -> Vec { - // TODO this is ugly. // create a list of num_windows - 1 sorted random numbers and append/prepend 0 and 1 let mut tmp = (0..num_windows-1).map(|_| rand::random::()).collect::>(); tmp.sort_by(|a,b| { a.partial_cmp(b).unwrap() }); @@ -21,23 +23,31 @@ fn generate_random_weights(num_windows: usize) -> Vec { return weights } - +// Generate a random weighted dataset from the given dataset by changing the weights fn generate_random_weighted_dataset(ds: Dataset) -> Dataset { let weights = generate_random_weights(ds.num_windows); - Dataset::new_weighted(ds, weights ) + Dataset::new_weighted(ds, weights) } +// Perform bootstrap error analysis. This runs the WHAM analysis num_runs times on random weighted +// datasets. The standard deviation is calculated on the bootstrapped probabilities of each bin. The +// standard deviation of the free eneergy is then deduced by error propagation (A_std = kT*1/P*P_std) pub fn run_bootstrap(cfg: &Config, ds: Dataset, P: &[f64], num_runs: usize) -> (Vec,Vec) { - let Ps: Vec> = (0..num_runs).map(|x| { + // Calculate bootstrapped probabilities + let bootstrapped_Ps: Vec> = (0..num_runs).map(|x| { println!("Bootstrap run {}/{}", x, num_runs); let rnd_weighted_dataset = generate_random_weighted_dataset(ds.clone()); perform_wham(cfg, &rnd_weighted_dataset).unwrap().0 }).collect(); + + // Evaulate standard deviation of P per bin let mut P_std = vec![0.0; ds.num_bins]; for bin in 0..ds.num_bins { - let Ps = Ps.iter().map(|window| window[bin]).collect::>(); + let Ps = bootstrapped_Ps.iter().map(|window| window[bin]).collect::>(); P_std[bin] = statistics::sd(&Ps, 1, num_runs); } + + // A_std by error propagation let A_std = P_std.iter().zip(P.iter()).map(|(std,P)| ds.kT*1.0/P*std).collect(); (P_std, A_std) }