use super::inversecumulativersg::InverseCumulativeRsg;
use super::mt19937uniformrng::MersenneTwisterUniformRng;
use super::randomsequencegenerator::RandomSequenceGenerator;
use crate::errors::QlResult;
use crate::math::distributions::normal::InverseCumulativeNormal;
use crate::methods::montecarlo::Sample;
use crate::types::Real;
pub trait SequenceGenerator {
fn next_sequence(&mut self) -> &Sample<Vec<Real>>;
fn last_sequence(&self) -> &Sample<Vec<Real>>;
fn dimension(&self) -> usize;
}
pub trait InverseCumulative {
fn evaluate(&self, x: Real) -> Real;
}
impl InverseCumulative for InverseCumulativeNormal {
fn evaluate(&self, x: Real) -> Real {
self.value(x)
.expect("inverse cumulative normal is finite for a uniform deviate in (0, 1)")
}
}
pub trait McRngTraits {
type RsgType: SequenceGenerator;
const ALLOWS_ERROR_ESTIMATE: bool;
fn make_sequence_generator(dimension: usize, seed: u32) -> QlResult<Self::RsgType>;
}
pub struct PseudoRandom;
impl McRngTraits for PseudoRandom {
type RsgType = InverseCumulativeRsg<
RandomSequenceGenerator<MersenneTwisterUniformRng>,
InverseCumulativeNormal,
>;
const ALLOWS_ERROR_ESTIMATE: bool = true;
fn make_sequence_generator(dimension: usize, seed: u32) -> QlResult<Self::RsgType> {
let ursg = RandomSequenceGenerator::with_seed(dimension, seed)?;
Ok(InverseCumulativeRsg::new(
ursg,
InverseCumulativeNormal::standard(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn allows_error_estimate<R: McRngTraits>() -> bool {
R::ALLOWS_ERROR_ESTIMATE
}
#[test]
fn pseudo_random_allows_an_error_estimate() {
assert!(allows_error_estimate::<PseudoRandom>());
}
#[test]
fn same_seed_generators_are_identical_across_draws() {
let mut a = PseudoRandom::make_sequence_generator(3, 42).unwrap();
let mut b = PseudoRandom::make_sequence_generator(3, 42).unwrap();
for _ in 0..5 {
assert_eq!(a.next_sequence().value, b.next_sequence().value);
}
}
#[test]
fn a_different_seed_diverges() {
let mut a = PseudoRandom::make_sequence_generator(3, 42).unwrap();
let mut c = PseudoRandom::make_sequence_generator(3, 43).unwrap();
assert_ne!(a.next_sequence().value, c.next_sequence().value);
}
#[test]
fn zero_dimension_is_rejected() {
assert!(PseudoRandom::make_sequence_generator(0, 42).is_err());
}
}