use core::marker::PhantomData;
use crate::error::IntegrateError;
use crate::numerical_integration::integrator::*;
use crate::numerical_integration::mode::IterativeMethod;
use crate::scalar::Numeric;
use crate::utils::summation::{Acc, SummationMethod};
pub const DEFAULT_TOTAL_ITERATIONS: u64 = 120;
#[derive(Debug, Clone, Copy)]
pub struct IterativeConfig {
pub total_iterations: u64,
pub integration_method: IterativeMethod,
pub summation: SummationMethod,
}
impl Default for IterativeConfig {
fn default() -> Self {
IterativeConfig {
total_iterations: DEFAULT_TOTAL_ITERATIONS,
integration_method: IterativeMethod::Booles,
summation: SummationMethod::Pairwise,
}
}
}
impl IterativeConfig {
pub fn from_parameters(total_iterations: u64, integration_method: IterativeMethod) -> Self {
IterativeConfig {
total_iterations,
integration_method,
summation: SummationMethod::Pairwise,
}
}
fn check_for_errors<T: Numeric, const NUM_INTEGRATIONS: usize>(
&self,
integration_limit: &[[T; 2]; NUM_INTEGRATIONS],
) -> Result<(), IntegrateError> {
if self.total_iterations == 0 {
return Err(IntegrateError::IterationsZero);
}
for limit in integration_limit {
classify(limit)?;
}
Ok(())
}
}
fn integrate_rule<T: Numeric, G: FnMut(T) -> Result<T, IntegrateError>>(
method: IterativeMethod,
iterations: u64,
lo: T,
hi: T,
g: G,
summation: SummationMethod,
) -> Result<T, IntegrateError> {
match method {
IterativeMethod::Booles => booles(iterations, lo, hi, g, summation),
IterativeMethod::Simpsons => simpsons(iterations, lo, hi, g, summation),
IterativeMethod::Trapezoidal => trapezoidal(iterations, lo, hi, g, summation),
}
}
fn booles<T: Numeric, G: FnMut(T) -> Result<T, IntegrateError>>(
iterations: u64,
lo: T,
hi: T,
mut g: G,
summation: SummationMethod,
) -> Result<T, IntegrateError> {
let delta = (hi - lo) / T::from_u64(iterations);
let mut point = lo;
let mut ans = Acc::new(summation);
ans.add(T::from_f64(7.0) * g(point)?);
let mut multiplier = T::from_f64(32.0);
for iter in 0..iterations - 1 {
point += delta;
ans.add(multiplier * g(point)?);
if (iter + 2) % 2 != 0 {
multiplier = T::from_f64(32.0);
} else if (iter + 2) % 4 == 0 {
multiplier = T::from_f64(14.0);
} else {
multiplier = T::from_f64(12.0);
}
}
ans.add(T::from_f64(7.0) * g(hi)?);
Ok(T::TWO * delta * ans.total() / T::from_f64(45.0))
}
fn simpsons<T: Numeric, G: FnMut(T) -> Result<T, IntegrateError>>(
iterations: u64,
lo: T,
hi: T,
mut g: G,
summation: SummationMethod,
) -> Result<T, IntegrateError> {
let delta = (hi - lo) / T::from_u64(iterations);
let mut point = lo;
let mut ans = Acc::new(summation);
ans.add(g(point)?);
let mut multiplier = T::from_f64(3.0);
for iter in 0..iterations - 1 {
point += delta;
ans.add(multiplier * g(point)?);
if (iter + 2) % 3 == 0 {
multiplier = T::TWO;
} else {
multiplier = T::from_f64(3.0);
}
}
ans.add(g(hi)?);
Ok(T::from_f64(3.0) * delta * ans.total() / T::from_f64(8.0))
}
fn trapezoidal<T: Numeric, G: FnMut(T) -> Result<T, IntegrateError>>(
iterations: u64,
lo: T,
hi: T,
mut g: G,
summation: SummationMethod,
) -> Result<T, IntegrateError> {
let delta = (hi - lo) / T::from_u64(iterations);
let mut point = lo;
let mut ans = Acc::new(summation);
ans.add(g(point)?);
for _ in 0..iterations - 1 {
point += delta;
ans.add(T::TWO * g(point)?);
}
ans.add(g(hi)?);
Ok(T::HALF * delta * ans.total())
}
#[derive(Debug, Clone, Copy)]
pub struct IterativeSingle<T = f64> {
pub config: IterativeConfig,
_marker: PhantomData<T>,
}
impl<T> Default for IterativeSingle<T> {
fn default() -> Self {
IterativeSingle {
config: IterativeConfig::default(),
_marker: PhantomData,
}
}
}
impl<T> IterativeSingle<T> {
pub fn from_parameters(total_iterations: u64, integration_method: IterativeMethod) -> Self {
IterativeSingle {
config: IterativeConfig::from_parameters(total_iterations, integration_method),
_marker: PhantomData,
}
}
pub fn with_kahan_summation(mut self) -> Self {
self.config.summation = SummationMethod::Kahan;
self
}
}
impl<T: Numeric> IterativeSingle<T> {
fn integrate<F: Fn(T) -> T, const NUM_INTEGRATIONS: usize>(
&self,
level: usize,
func: &F,
integration_limit: &[[T; 2]; NUM_INTEGRATIONS],
) -> Result<T, IntegrateError> {
let method = self.config.integration_method;
let iterations = self.config.total_iterations;
let summation = self.config.summation;
let domain = classify(&integration_limit[level - 1])?;
if level == 1 {
return match domain {
Domain::Finite(a, b) => {
integrate_rule(method, iterations, a, b, |x| is_finite(func(x)), summation)
}
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(
method,
iterations,
lo,
hi,
|t| {
let (x, jacobian) = map_sample(&domain, t);
is_finite(func(x) * jacobian)
},
summation,
)
}
};
}
let inner = self.integrate(level - 1, func, integration_limit)?;
match domain {
Domain::Finite(a, b) => {
integrate_rule(method, iterations, a, b, |_| is_finite(inner), summation)
}
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(
method,
iterations,
lo,
hi,
|t| {
let (_, jacobian) = map_sample(&domain, t);
is_finite(inner * jacobian)
},
summation,
)
}
}
}
}
impl<T: Numeric> IntegratorSingleVariable for IterativeSingle<T> {
type Scalar = T;
fn integrate<F: Fn(T) -> T, const NUM_INTEGRATIONS: usize>(
&self,
func: &F,
integration_limit: &[[T; 2]; NUM_INTEGRATIONS],
) -> Result<T, IntegrateError> {
self.config.check_for_errors(integration_limit)?;
self.integrate(NUM_INTEGRATIONS, func, integration_limit)
}
}
#[derive(Debug, Clone, Copy)]
pub struct IterativeMulti<T = f64> {
pub config: IterativeConfig,
_marker: PhantomData<T>,
}
impl<T> Default for IterativeMulti<T> {
fn default() -> Self {
IterativeMulti {
config: IterativeConfig::default(),
_marker: PhantomData,
}
}
}
impl<T> IterativeMulti<T> {
pub fn from_parameters(total_iterations: u64, integration_method: IterativeMethod) -> Self {
IterativeMulti {
config: IterativeConfig::from_parameters(total_iterations, integration_method),
_marker: PhantomData,
}
}
pub fn with_kahan_summation(mut self) -> Self {
self.config.summation = SummationMethod::Kahan;
self
}
}
impl<T: Numeric> IterativeMulti<T> {
fn integrate<
F: Fn(&[T; NUM_VARS]) -> T,
const NUM_VARS: usize,
const NUM_INTEGRATIONS: usize,
>(
&self,
level: usize,
idx_to_integrate: [usize; NUM_INTEGRATIONS],
func: &F,
integration_limits: &[[T; 2]; NUM_INTEGRATIONS],
point: &[T; NUM_VARS],
) -> Result<T, IntegrateError> {
let method = self.config.integration_method;
let iterations = self.config.total_iterations;
let summation = self.config.summation;
let domain = classify(&integration_limits[level - 1])?;
let var = idx_to_integrate[level - 1];
if level == 1 {
let mut current = *point;
return match domain {
Domain::Finite(a, b) => integrate_rule(
method,
iterations,
a,
b,
|x| {
current[var] = x;
is_finite(func(¤t))
},
summation,
),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(
method,
iterations,
lo,
hi,
|t| {
let (x, jacobian) = map_sample(&domain, t);
current[var] = x;
is_finite(func(¤t) * jacobian)
},
summation,
)
}
};
}
let mut current = *point;
match domain {
Domain::Finite(a, b) => integrate_rule(
method,
iterations,
a,
b,
|x| {
current[var] = x;
self.integrate(
level - 1,
idx_to_integrate,
func,
integration_limits,
¤t,
)
},
summation,
),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(
method,
iterations,
lo,
hi,
|t| {
let (x, jacobian) = map_sample(&domain, t);
current[var] = x;
let inner = self.integrate(
level - 1,
idx_to_integrate,
func,
integration_limits,
¤t,
)?;
is_finite(inner * jacobian)
},
summation,
)
}
}
}
}
impl<T: Numeric> IntegratorMultiVariable for IterativeMulti<T> {
type Scalar = T;
fn integrate<
F: Fn(&[T; NUM_VARS]) -> T,
const NUM_VARS: usize,
const NUM_INTEGRATIONS: usize,
>(
&self,
idx_to_integrate: [usize; NUM_INTEGRATIONS],
func: &F,
integration_limits: &[[T; 2]; NUM_INTEGRATIONS],
point: &[T; NUM_VARS],
) -> Result<T, IntegrateError> {
self.config.check_for_errors(integration_limits)?;
for &idx in &idx_to_integrate {
if idx >= NUM_VARS {
return Err(IntegrateError::IndexOutOfRange);
}
}
self.integrate(
NUM_INTEGRATIONS,
idx_to_integrate,
func,
integration_limits,
point,
)
}
}