use std::cell::RefCell;
use crate::types::{Real, Size};
use super::momentbasedgaussianpolynomial::{MomentBasedPolynomial, memoized};
struct TrigonometricMoments {
u: Real,
seed0: Real,
seed1: Real,
m: RefCell<Vec<Real>>,
f: RefCell<Vec<Real>>,
}
impl TrigonometricMoments {
fn new(u: Real, seed0: Real, seed1: Real) -> Self {
TrigonometricMoments {
u,
seed0,
seed1,
m: RefCell::new(Vec::new()),
f: RefCell::new(Vec::new()),
}
}
fn moment_(&self, n: Size) -> Real {
memoized(&self.m, n, || match n {
0 => self.seed0,
1 => self.seed1,
_ => {
let n_ = n as Real;
(2.0 * n_ * self.moment_(n - 1) - n_ * (n_ - 1.0) * self.moment_(n - 2))
/ (1.0 + self.u * self.u)
}
})
}
fn fact(&self, n: Size) -> Real {
memoized(&self.f, n, || {
if n == 0 {
1.0
} else {
n as Real * self.fact(n - 1)
}
})
}
}
pub struct GaussLaguerreCosinePolynomial {
moments: TrigonometricMoments,
m0: Real,
}
impl GaussLaguerreCosinePolynomial {
pub fn new(u: Real) -> Self {
let u2 = u * u;
let seed0 = 1.0 / (1.0 + u2);
let seed1 = (1.0 - u2) / ((1.0 + u2) * (1.0 + u2));
GaussLaguerreCosinePolynomial {
moments: TrigonometricMoments::new(u, seed0, seed1),
m0: 1.0 + 1.0 / (1.0 + u2),
}
}
}
impl MomentBasedPolynomial for GaussLaguerreCosinePolynomial {
fn moment(&self, i: Size) -> Real {
(self.moments.moment_(i) + self.moments.fact(i)) / self.m0
}
fn w(&self, x: Real) -> Real {
(-x).exp() * (1.0 + (self.moments.u * x).cos()) / self.m0
}
}
pub struct GaussLaguerreSinePolynomial {
moments: TrigonometricMoments,
m0: Real,
}
impl GaussLaguerreSinePolynomial {
pub fn new(u: Real) -> Self {
let u2 = u * u;
let seed0 = u / (1.0 + u2);
let seed1 = 2.0 * u / ((1.0 + u2) * (1.0 + u2));
GaussLaguerreSinePolynomial {
moments: TrigonometricMoments::new(u, seed0, seed1),
m0: 1.0 + u / (1.0 + u2),
}
}
}
impl MomentBasedPolynomial for GaussLaguerreSinePolynomial {
fn moment(&self, i: Size) -> Real {
(self.moments.moment_(i) + self.moments.fact(i)) / self.m0
}
fn w(&self, x: Real) -> Real {
(-x).exp() * (1.0 + (self.moments.u * x).sin()) / self.m0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::math::integrals::gaussianquadratures::GaussianQuadrature;
use crate::math::integrals::momentbasedgaussianpolynomial::MomentBasedGaussianPolynomial;
use crate::types::Real;
fn test_single(quad: &GaussianQuadrature, tag: &str, f: fn(Real) -> Real, expected: Real) {
let calculated = quad.integrate(f);
assert!(
(calculated - expected).abs() <= 1.0e-4,
"integrating {tag}: calculated {calculated}, expected {expected}"
);
}
fn inv_exp(x: Real) -> Real {
(-x).exp()
}
fn x_inv_exp(x: Real) -> Real {
x * (-x).exp()
}
#[test]
fn gauss_laguerre_cosine_quadrature() {
let poly = MomentBasedGaussianPolynomial::new(GaussLaguerreCosinePolynomial::new(0.2));
let quad = GaussianQuadrature::new(16, &poly).expect("16 > 0");
test_single(&quad, "f(x) = exp(-x)", inv_exp, 1.0);
test_single(&quad, "f(x) = x*exp(-x)", x_inv_exp, 1.0);
}
#[test]
fn gauss_laguerre_sine_quadrature() {
let poly = MomentBasedGaussianPolynomial::new(GaussLaguerreSinePolynomial::new(0.2));
let quad = GaussianQuadrature::new(16, &poly).expect("16 > 0");
test_single(&quad, "f(x) = exp(-x)", inv_exp, 1.0);
test_single(&quad, "f(x) = x*exp(-x)", x_inv_exp, 1.0);
}
}