extern crate emcee;
extern crate rand;
use std::fs::File;
use std::io::{BufWriter, Write};
use rand::distributions::{Range, Normal, IndependentSample};
use rand::{StdRng, SeedableRng};
use emcee::{Guess, Prob};
fn sort(data: &mut Vec<f32>) {
data.sort_by(|a, b| a.partial_cmp(b).unwrap());
}
fn compute_quantiles(chain: &[Guess]) -> Vec<[f32; 3]> {
let nparams = chain[0].values.len();
let niterations = chain.len();
let mut param_vecs: Vec<Vec<f32>> = vec![Vec::with_capacity(chain.len()); nparams];
for guess in chain {
for param in 0..nparams {
param_vecs[param].push(guess.values[param]);
}
}
let mut out = Vec::with_capacity(nparams);
let lower_idx = (0.16 * niterations as f32) as usize;
let med_idx = (0.5 * niterations as f32) as usize;
let upper_idx = (0.84 * niterations as f32) as usize;
for param in 0..nparams {
sort(&mut param_vecs[param]);
let med = param_vecs[param][med_idx];
let lower = param_vecs[param][lower_idx];
let upper = param_vecs[param][upper_idx];
let res = [lower, med, upper];
out.push(res);
}
out
}
fn main() {
let mut rng = StdRng::from_seed(&[42]);
let unit_range = Range::new(0f32, 1f32);
let norm_gen = Normal::new(0.0, 1.0);
let m_true = -0.9594f32;
let b_true = 4.294f32;
let f_true = 0.534f32;
let npoints = 50usize;
let x = {
let mut unsorted: Vec<_> = (0..npoints)
.map(|_| 10f32 * unit_range.ind_sample(&mut rng))
.collect();
unsorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
unsorted
};
let mut y = Vec::with_capacity(npoints);
let mut yerr = Vec::with_capacity(npoints);
for xval in &x {
let yerr_val = 0.1 + 0.5 * unit_range.ind_sample(&mut rng);
let mut y_val = m_true * xval + b_true;
y_val += (f_true * y_val).abs() * norm_gen.ind_sample(&mut rng) as f32;
y_val += yerr_val * norm_gen.ind_sample(&mut rng) as f32;
y.push(y_val);
yerr.push(yerr_val);
}
let guess = Guess::new(&[-1.003, 4.528, 0.454f32.ln()]);
struct LinearWithUnderestimatedErrors<'a> {
x: &'a [f32],
y: &'a [f32],
e: &'a [f32],
};
impl<'a> Prob for LinearWithUnderestimatedErrors<'a> {
fn lnlike(&self, theta: &Guess) -> f32 {
assert_eq!(theta.values.len(), 3);
assert_eq!(self.x.len(), self.y.len());
assert_eq!(self.y.len(), self.e.len());
let m = theta[0];
let b = theta[1];
let lnf = theta[2];
let mut result = 0.;
for i in 0..self.x.len() {
let model = m * self.x[i] + b;
let inv_sigma2 = 1.0 / (self.e[i].powf(2.0) + model.powf(2.0) * (2.0 * lnf).exp());
result += (self.y[i] - model).powf(2.) * inv_sigma2 - inv_sigma2.ln();
}
-0.5 * result
}
fn lnprior(&self, theta: &Guess) -> f32 {
assert_eq!(theta.values.len(), 3);
let m = theta[0];
let b = theta[1];
let lnf = theta[2];
if (m > -5.0) && (m < 5.0) && (b > 0.0) && (b < 10.0) && (lnf > -10.0) && (lnf < 1.0) {
0.
} else {
-std::f32::INFINITY
}
}
}
let model = LinearWithUnderestimatedErrors {
x: &x,
y: &y,
e: &yerr,
};
let ndim = 3;
let nwalkers = 100;
let pos = guess.create_initial_guess_with_rng(nwalkers, &mut rng);
let mut sampler = emcee::EnsembleSampler::new(nwalkers, ndim, &model)
.expect("creating sampler");
sampler.seed(&[42]);
sampler.run_mcmc(&pos, 500).unwrap();
let flatchain = sampler.flatchain();
let file = File::create("/tmp/emcee-results.txt").expect("opening output file");
let mut writer = BufWriter::new(&file);
for (i, guess) in flatchain.iter().enumerate() {
if i < 50 * nwalkers {
continue;
}
write!(&mut writer, "{} {} {}\n", guess[0], guess[1], guess[2])
.expect("writing output line");
}
let marginalised_posteriors = compute_quantiles(&flatchain);
print_marginalised("m", &marginalised_posteriors[0], m_true);
print_marginalised("b", &marginalised_posteriors[1], b_true);
print_marginalised("lnf", &marginalised_posteriors[2], f_true.ln());
}
fn print_marginalised(name: &str, values: &[f32], truth: f32) {
println!("{:3} = {:6.3} +{:.3} -{:.3} (truth: {:6.3})",
name,
values[1],
values[1] - values[0],
values[2] - values[1],
truth);
}