use crate::deriv::{DerivationLog, DerivedExpr, RewriteStep};
use crate::kernel::{ExprId, ExprPool};
use crate::simplify::simplify;
use super::dists;
use super::verify;
use super::{div, sub, DistKind, Distribution, ProbError};
pub fn characteristic_function(
dist: &Distribution,
t: ExprId,
pool: &ExprPool,
) -> Result<DerivedExpr<ExprId>, ProbError> {
super::stash_prob_side_conditions(Vec::new());
let claim = simplify(claim_for(dist, t, pool)?, pool).value;
let (evidence, fourier_agreed) = verify::check_characteristic(claim, t, dist, pool)?;
let mut log = DerivationLog::new();
log.push(RewriteStep::simple(
"prob_characteristic_function",
t,
claim,
));
if fourier_agreed {
log.push(RewriteStep::simple(
"prob_charfun_matches_fourier_transform_at_minus_t_over_two_pi",
claim,
claim,
));
}
log.push(verify::evidence_step(&evidence, claim, dist, pool));
super::stash_prob_side_conditions(super::conditions_of(&log));
Ok(DerivedExpr::with_log(claim, log))
}
fn imag(pool: &ExprPool) -> ExprId {
pool.imaginary_unit()
}
fn exp(arg: ExprId, pool: &ExprPool) -> ExprId {
pool.func("exp", vec![arg])
}
fn claim_for(dist: &Distribution, t: ExprId, pool: &ExprPool) -> Result<ExprId, ProbError> {
let p = dist.params();
let i = imag(pool);
let one = pool.integer(1);
let two = pool.integer(2);
let neg1 = pool.integer(-1);
Ok(match dist.kind() {
DistKind::Normal => exp(
pool.add(vec![
pool.mul(vec![i, p[0], t]),
pool.mul(vec![
pool.rational(-1, 2),
pool.pow(p[1], two),
pool.pow(t, two),
]),
]),
pool,
),
DistKind::Uniform => div(
sub(
exp(pool.mul(vec![i, p[1], t]), pool),
exp(pool.mul(vec![i, p[0], t]), pool),
pool,
),
pool.mul(vec![i, t, sub(p[1], p[0], pool)]),
pool,
),
DistKind::Exponential => div(p[0], sub(p[0], pool.mul(vec![i, t]), pool), pool),
DistKind::Gamma => pool.pow(
sub(one, pool.mul(vec![i, p[1], t]), pool),
pool.mul(vec![neg1, p[0]]),
),
DistKind::Bernoulli => pool.add(vec![
sub(one, p[0], pool),
pool.mul(vec![p[0], exp(pool.mul(vec![i, t]), pool)]),
]),
DistKind::Binomial => pool.pow(
pool.add(vec![
sub(one, p[1], pool),
pool.mul(vec![p[1], exp(pool.mul(vec![i, t]), pool)]),
]),
p[0],
),
DistKind::Poisson => exp(
pool.mul(vec![p[0], sub(exp(pool.mul(vec![i, t]), pool), one, pool)]),
pool,
),
DistKind::LogNormal => {
return Err(ProbError::NoClosedForm {
quantity: "the log-normal characteristic function",
missing: "nothing this library could add — φ for a log-normal has no closed \
form in elementary or standard special functions, and its moment \
series diverges for every t ≠ 0",
})
}
DistKind::Beta => {
return Err(ProbError::NoClosedForm {
quantity: "the Beta characteristic function",
missing: "the confluent hypergeometric function ₁F₁(α; α+β; it)",
})
}
})
}
pub(crate) fn fourier_ready_density(
dist: &Distribution,
x: ExprId,
pool: &ExprPool,
) -> Option<ExprId> {
match dist.kind() {
DistKind::Normal => Some(dists::pdf(dist, x, pool)),
DistKind::Exponential => Some(pool.mul(vec![
pool.func("heaviside", vec![x]),
dists::pdf(dist, x, pool),
])),
_ => None,
}
}