use std::ops::{Add, Div, Mul, Neg, Sub};
use num_traits::{Float, FloatConst, One, Zero};
use rand::distributions::Uniform;
use rand_distr::Distribution;
#[cfg(feature = "serde-serialize")]
use serde::{Deserialize, Serialize};
use super::super::{su2, su3, CMatrix2, CMatrix3, Real};
#[derive(Clone, Debug, Copy, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct ModifiedNormal<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Neg<Output = T>
+ Float
+ FloatConst
+ PartialOrd,
rand::distributions::OpenClosed01: Distribution<T>,
{
param_exp: T,
}
impl<T> ModifiedNormal<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Neg<Output = T>
+ Float
+ FloatConst
+ Zero
+ PartialOrd,
rand::distributions::OpenClosed01: Distribution<T>,
{
getter_copy!(
pub const,
param_exp,
T
);
pub fn new(param_exp: T) -> Option<Self> {
if param_exp.le(&T::zero()) || param_exp.is_infinite() || param_exp.is_nan() {
return None;
}
Some(Self { param_exp })
}
}
impl<T> Distribution<T> for ModifiedNormal<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Neg<Output = T>
+ Float
+ FloatConst
+ rand_distr::uniform::SampleUniform
+ PartialOrd,
rand::distributions::OpenClosed01: Distribution<T>,
{
fn sample<R>(&self, rng: &mut R) -> T
where
R: rand::Rng + ?Sized,
{
let mut r = [T::one(); 3];
for element in r.iter_mut() {
*element = rng.sample(rand::distributions::OpenClosed01);
}
let two = T::one() + T::one();
(-(r[0].ln() + (two * T::PI() * r[1]).cos().powi(2) * r[2].ln()) / (two * self.param_exp()))
.sqrt()
}
}
impl<T> std::fmt::Display for ModifiedNormal<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Neg<Output = T>
+ Float
+ FloatConst
+ PartialOrd
+ std::fmt::Display,
rand::distributions::OpenClosed01: Distribution<T>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"modified normal distribution with parameter {}",
self.param_exp()
)
}
}
#[derive(Clone, Debug, Copy, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct HeatBathDistribution<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Sub<T, Output = T>
+ Neg<Output = T>
+ Float
+ FloatConst
+ Zero
+ rand_distr::uniform::SampleUniform
+ PartialOrd,
rand::distributions::OpenClosed01: Distribution<T>,
Uniform<T>: Distribution<T>,
{
param_exp: T,
}
impl<T> HeatBathDistribution<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Sub<T, Output = T>
+ Neg<Output = T>
+ Float
+ FloatConst
+ Zero
+ rand_distr::uniform::SampleUniform
+ PartialOrd,
rand::distributions::OpenClosed01: Distribution<T>,
Uniform<T>: Distribution<T>,
{
getter_copy!(
pub const,
param_exp,
T
);
pub fn new(param_exp: T) -> Option<Self> {
if param_exp.le(&T::zero()) || param_exp.is_infinite() || param_exp.is_nan() {
return None;
}
Some(Self { param_exp })
}
}
impl Distribution<CMatrix2> for HeatBathDistribution<f64> {
fn sample<R>(&self, rng: &mut R) -> CMatrix2
where
R: rand::Rng + ?Sized,
{
let distr_norm = HeatBathDistributionNorm::new(self.param_exp()).expect("unreachable");
let x0: f64 = rng.sample(&distr_norm);
let uniform = Uniform::new(-1_f64, 1_f64);
let mut x_unorm = na::Vector3::from_fn(|_, _| rng.sample(&uniform));
while x_unorm.norm() <= f64::EPSILON {
x_unorm = na::Vector3::from_fn(|_, _| rng.sample(&uniform));
}
let x =
x_unorm.try_normalize(f64::EPSILON).expect("unreachable") * (1_f64 - x0 * x0).sqrt();
su2::complex_matrix_from_vec(x0, x)
}
}
impl<T> std::fmt::Display for HeatBathDistribution<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Sub<T, Output = T>
+ Neg<Output = T>
+ Float
+ FloatConst
+ Zero
+ rand_distr::uniform::SampleUniform
+ PartialOrd
+ std::fmt::Display,
rand::distributions::OpenClosed01: Distribution<T>,
Uniform<T>: Distribution<T>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"heat bath distribution with parameter {}",
self.param_exp()
)
}
}
#[derive(Clone, Debug, Copy, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct HeatBathDistributionNorm<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Sub<T, Output = T>
+ Neg<Output = T>
+ Float
+ FloatConst
+ Zero
+ rand_distr::uniform::SampleUniform
+ PartialOrd,
rand::distributions::OpenClosed01: Distribution<T>,
Uniform<T>: Distribution<T>,
{
param_exp: T,
}
impl<T> HeatBathDistributionNorm<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Neg<Output = T>
+ Sub<T, Output = T>
+ Float
+ FloatConst
+ Zero
+ rand_distr::uniform::SampleUniform
+ PartialOrd,
rand::distributions::OpenClosed01: Distribution<T>,
Uniform<T>: Distribution<T>,
{
getter_copy!(
pub const,
param_exp,
T
);
pub fn new(param_exp: T) -> Option<Self> {
if param_exp.le(&T::zero()) || param_exp.is_infinite() || param_exp.is_nan() {
return None;
}
Some(Self { param_exp })
}
}
impl<T> Distribution<T> for HeatBathDistributionNorm<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Neg<Output = T>
+ Sub<T, Output = T>
+ Float
+ FloatConst
+ Zero
+ rand_distr::uniform::SampleUniform
+ PartialOrd,
rand::distributions::OpenClosed01: Distribution<T>,
Uniform<T>: Distribution<T>,
{
fn sample<R>(&self, rng: &mut R) -> T
where
R: rand::Rng + ?Sized,
{
let two = T::one() + T::one();
let distributions = ModifiedNormal::new(self.param_exp()).unwrap();
loop {
let r = rng.sample(Uniform::new(T::zero(), T::one()));
let lambda = rng.sample(distributions);
if r.powi(2) <= T::one() - lambda.powi(2) {
return T::one() - two * lambda.powi(2);
}
}
}
}
impl<T> std::fmt::Display for HeatBathDistributionNorm<T>
where
T: One
+ Div<T, Output = T>
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Neg<Output = T>
+ Sub<T, Output = T>
+ Float
+ FloatConst
+ Zero
+ rand_distr::uniform::SampleUniform
+ PartialOrd
+ std::fmt::Display,
rand::distributions::OpenClosed01: Distribution<T>,
Uniform<T>: Distribution<T>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"heat bath norm distribution with parameter {}",
self.param_exp()
)
}
}
#[derive(Clone, Debug, Copy, PartialEq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct CloseToUnit {
spread_parameter: Real,
}
impl CloseToUnit {
getter_copy!(
pub const,
spread_parameter,
f64
);
pub fn new(spread_parameter: Real) -> Option<Self> {
if spread_parameter <= 0_f64 || spread_parameter >= 1_f64 || spread_parameter.is_nan() {
return None;
}
Some(Self { spread_parameter })
}
}
impl Distribution<CMatrix2> for CloseToUnit {
fn sample<R>(&self, rng: &mut R) -> CMatrix2
where
R: rand::Rng + ?Sized,
{
su2::random_su2_close_to_unity(self.spread_parameter, rng)
}
}
impl Distribution<CMatrix3> for CloseToUnit {
fn sample<R>(&self, rng: &mut R) -> CMatrix3
where
R: rand::Rng + ?Sized,
{
su3::random_su3_close_to_unity(self.spread_parameter, rng)
}
}
impl std::fmt::Display for CloseToUnit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"distribution closed to unit with spread parameter {}",
self.spread_parameter()
)
}
}
#[cfg(test)]
mod test {
use rand::{Rng, SeedableRng};
use super::*;
const SEED_RNG: u64 = 0x45_78_93_f4_4a_b0_67_f0;
#[test]
fn modified_normal() {
let mut rng = rand::rngs::StdRng::seed_from_u64(SEED_RNG);
for param in &[0.1_f64, 0.5_f64, 1_f64, 10_f64] {
let mn = ModifiedNormal::new(*param).unwrap();
for _ in 0_u32..1000_u32 {
assert!(rng.sample(&mn) >= 0_f64);
}
}
}
#[test]
#[allow(clippy::cognitive_complexity)]
fn distribution_creation() {
assert!(ModifiedNormal::new(0_f64).is_none());
assert!(ModifiedNormal::new(-1_f64).is_none());
assert!(ModifiedNormal::new(f64::NAN).is_none());
assert!(ModifiedNormal::new(f64::INFINITY).is_none());
assert!(ModifiedNormal::new(0.1_f64).is_some());
assert!(ModifiedNormal::new(2_f32).is_some());
let param = 0.5_f64;
let mn = ModifiedNormal::new(param).unwrap();
assert_eq!(mn.param_exp(), param);
assert_eq!(
mn.to_string(),
"modified normal distribution with parameter 0.5"
);
assert!(HeatBathDistributionNorm::new(0_f64).is_none());
assert!(HeatBathDistributionNorm::new(-1_f64).is_none());
assert!(HeatBathDistributionNorm::new(f64::NAN).is_none());
assert!(HeatBathDistributionNorm::new(f64::INFINITY).is_none());
assert!(HeatBathDistributionNorm::new(0.1_f64).is_some());
assert!(HeatBathDistributionNorm::new(2_f32).is_some());
let heat_bath_norm = HeatBathDistributionNorm::new(param).unwrap();
assert_eq!(heat_bath_norm.param_exp(), param);
assert_eq!(
heat_bath_norm.to_string(),
"heat bath norm distribution with parameter 0.5"
);
assert!(HeatBathDistribution::new(0_f64).is_none());
assert!(HeatBathDistribution::new(-1_f64).is_none());
assert!(HeatBathDistribution::new(f64::NAN).is_none());
assert!(HeatBathDistribution::new(f64::INFINITY).is_none());
assert!(HeatBathDistribution::new(0.1_f64).is_some());
assert!(HeatBathDistribution::new(2_f32).is_some());
let heat_bath = HeatBathDistribution::new(param).unwrap();
assert_eq!(heat_bath.param_exp(), param);
assert_eq!(
heat_bath.to_string(),
"heat bath distribution with parameter 0.5"
);
assert!(CloseToUnit::new(0_f64).is_none());
assert!(CloseToUnit::new(-1_f64).is_none());
assert!(CloseToUnit::new(f64::NAN).is_none());
assert!(CloseToUnit::new(f64::INFINITY).is_none());
assert!(CloseToUnit::new(2_f64).is_none());
assert!(CloseToUnit::new(0.5_f64).is_some());
let cu = CloseToUnit::new(param).unwrap();
assert_eq!(cu.spread_parameter(), param);
assert_eq!(
cu.to_string(),
"distribution closed to unit with spread parameter 0.5"
);
}
}