#![doc = include_str!("../README.md")]
#[rustfmt::skip]
mod coefficients;
use num_complex::Complex;
pub fn laplace_inversion(
laplace_func: impl Fn(Complex<f64>) -> Complex<f64>,
s: f64,
max_function_evals: usize,
) -> f64 {
assert!(
max_function_evals <= coefficients::MAX_EVALUATIONS,
"Laplace maximum function evaluations must be less or equal to {}",
coefficients::MAX_EVALUATIONS
);
let (mu1, eta_betas, first_eta) = coefficients::ETA_BETA_PAIRS[max_function_evals];
std::iter::once((first_eta.into(), mu1.into()))
.chain(eta_betas.iter().map(|(eta_re, eta_im, beta)| {
(Complex::new(*eta_re, *eta_im), Complex::new(mu1, *beta))
}))
.map(|(eta, beta)| (eta * laplace_func(beta / s)).re)
.sum::<f64>()
/ s
}
pub fn laplace_inversion_mut(
mut laplace_func: impl FnMut(Complex<f64>) -> Complex<f64>,
s: f64,
max_function_evals: usize,
) -> f64 {
assert!(
max_function_evals <= coefficients::MAX_EVALUATIONS,
"Laplace maximum function evaluations must be less or equal to {}",
coefficients::MAX_EVALUATIONS
);
let (mu1, eta_betas, first_eta) = coefficients::ETA_BETA_PAIRS[max_function_evals];
std::iter::once((first_eta.into(), mu1.into()))
.chain(eta_betas.iter().map(|(eta_re, eta_im, beta)| {
(Complex::new(*eta_re, *eta_im), Complex::new(mu1, *beta))
}))
.map(|(eta, beta)| (eta * laplace_func(beta / s)).re)
.sum::<f64>()
/ s
}
#[cfg(test)]
mod tests {
use num_complex::{Complex, ComplexFloat as _};
use super::*;
fn invert_fns(max_function_evals: usize) {
invert_fn(
"Exponential",
|s| (1.0 + s).recip(),
|s| (-s).exp(),
&[0.01, 0.1, 1.0, 10.0],
max_function_evals,
);
invert_fn(
"Sine",
|s| (1.0 + s.powi(2)).recip(),
|s| s.sin(),
&[0.1, 0.2, 1.0, 2.0, 4.0],
max_function_evals,
);
invert_fn(
"Staircase",
|s| s.recip() / (s.exp() - 1.0),
|s| s.floor(),
&[0.5, 1.5, 2.5, 3.5, 4.5],
max_function_evals,
);
}
fn invert_fn(
name: &str,
func: fn(time: Complex<f64>) -> Complex<f64>,
inverse: fn(time: f64) -> f64,
times: &[f64],
max_function_evals: usize,
) {
for time in times {
let result = laplace_inversion(func, *time, max_function_evals);
let compare = inverse(*time);
assert!(
approx::relative_eq!(result, compare, epsilon = 0.01),
"Inversion of function \"{name}\" failed:\n\tTime : {time}\n\tResult : {result}\n\tCompare : {compare}\n\tMax Evals: {max_function_evals}"
);
}
}
#[test]
fn laplace_inversions() {
invert_fns(30);
}
}