use core::marker::PhantomData;
use crate::numerical_integration::integrator::*;
use crate::numerical_integration::mode::IterativeMethod;
use crate::scalar::Numeric;
use crate::utils::error_codes::CalcError;
use crate::utils::summation::PairwiseSum;
pub const DEFAULT_TOTAL_ITERATIONS: u64 = 120;
#[derive(Debug, Clone, Copy)]
pub struct IterativeConfig {
pub total_iterations: u64,
pub integration_method: IterativeMethod,
}
impl Default for IterativeConfig {
fn default() -> Self {
IterativeConfig {
total_iterations: DEFAULT_TOTAL_ITERATIONS,
integration_method: IterativeMethod::Booles,
}
}
}
impl IterativeConfig {
pub fn from_parameters(total_iterations: u64, integration_method: IterativeMethod) -> Self {
IterativeConfig {
total_iterations,
integration_method,
}
}
fn check_for_errors<T: Numeric, const NUM_INTEGRATIONS: usize>(
&self,
integration_limit: &[[T; 2]; NUM_INTEGRATIONS],
) -> Result<(), CalcError> {
if self.total_iterations == 0 {
return Err(CalcError::IterationsZero);
}
for limit in integration_limit {
classify(limit)?;
}
Ok(())
}
}
fn integrate_rule<T: Numeric, G: FnMut(T) -> T>(
method: IterativeMethod,
iterations: u64,
lo: T,
hi: T,
g: G,
) -> T {
match method {
IterativeMethod::Booles => booles(iterations, lo, hi, g),
IterativeMethod::Simpsons => simpsons(iterations, lo, hi, g),
IterativeMethod::Trapezoidal => trapezoidal(iterations, lo, hi, g),
}
}
fn booles<T: Numeric, G: FnMut(T) -> T>(iterations: u64, lo: T, hi: T, mut g: G) -> T {
let delta = (hi - lo) / T::from_u64(iterations);
let mut point = lo;
let mut ans = PairwiseSum::new();
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));
T::TWO * delta * ans.total() / T::from_f64(45.0)
}
fn simpsons<T: Numeric, G: FnMut(T) -> T>(iterations: u64, lo: T, hi: T, mut g: G) -> T {
let delta = (hi - lo) / T::from_u64(iterations);
let mut point = lo;
let mut ans = PairwiseSum::new();
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));
T::from_f64(3.0) * delta * ans.total() / T::from_f64(8.0)
}
fn trapezoidal<T: Numeric, G: FnMut(T) -> T>(iterations: u64, lo: T, hi: T, mut g: G) -> T {
let delta = (hi - lo) / T::from_u64(iterations);
let mut point = lo;
let mut ans = PairwiseSum::new();
ans.add(g(point));
for _ in 0..iterations - 1 {
point += delta;
ans.add(T::TWO * g(point));
}
ans.add(g(hi));
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,
}
}
}
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],
) -> T {
let method = self.config.integration_method;
let iterations = self.config.total_iterations;
let domain = match classify(&integration_limit[level - 1]) {
Ok(d) => d,
Err(_) => return T::NAN, };
if level == 1 {
return match domain {
Domain::Finite(a, b) => integrate_rule(method, iterations, a, b, func),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(method, iterations, lo, hi, |t| {
let (x, jacobian) = map_sample(&domain, t);
func(x) * jacobian
})
}
};
}
let inner = self.integrate(level - 1, func, integration_limit);
match domain {
Domain::Finite(a, b) => integrate_rule(method, iterations, a, b, |_| inner),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(method, iterations, lo, hi, |t| {
let (_, jacobian) = map_sample(&domain, t);
inner * jacobian
})
}
}
}
}
impl<T: Numeric> IntegratorSingleVariable for IterativeSingle<T> {
type Scalar = T;
fn get<F: Fn(T) -> T, const NUM_INTEGRATIONS: usize>(
&self,
func: &F,
integration_limit: &[[T; 2]; NUM_INTEGRATIONS],
) -> Result<T, CalcError> {
self.config.check_for_errors(integration_limit)?;
Ok(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,
}
}
}
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],
) -> T {
let method = self.config.integration_method;
let iterations = self.config.total_iterations;
let domain = match classify(&integration_limits[level - 1]) {
Ok(d) => d,
Err(_) => return T::NAN, };
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;
func(¤t)
}),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(method, iterations, lo, hi, |t| {
let (x, jacobian) = map_sample(&domain, t);
current[var] = x;
func(¤t) * jacobian
})
}
};
}
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,
)
}),
_ => {
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,
);
inner * jacobian
})
}
}
}
}
impl<T: Numeric> IntegratorMultiVariable for IterativeMulti<T> {
type Scalar = T;
fn get<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, CalcError> {
self.config.check_for_errors(integration_limits)?;
Ok(self.integrate(
NUM_INTEGRATIONS,
idx_to_integrate,
func,
integration_limits,
point,
))
}
}