use crate::DType;
use crate::stats::StatsResult;
use numr::algorithm::special::SpecialFunctions;
use numr::error::Result;
use numr::ops::{ScalarOps, TensorOps};
use numr::runtime::{Runtime, RuntimeClient};
use numr::tensor::Tensor;
pub trait Distribution {
fn mean(&self) -> f64;
fn var(&self) -> f64;
fn std(&self) -> f64 {
self.var().sqrt()
}
fn entropy(&self) -> f64;
fn median(&self) -> f64;
fn mode(&self) -> f64;
fn skewness(&self) -> f64;
fn kurtosis(&self) -> f64;
}
pub trait ContinuousDistribution: Distribution {
fn pdf(&self, x: f64) -> f64;
fn log_pdf(&self, x: f64) -> f64 {
self.pdf(x).ln()
}
fn cdf(&self, x: f64) -> f64;
fn sf(&self, x: f64) -> f64 {
1.0 - self.cdf(x)
}
fn log_cdf(&self, x: f64) -> f64 {
self.cdf(x).ln()
}
fn ppf(&self, p: f64) -> StatsResult<f64>;
fn isf(&self, p: f64) -> StatsResult<f64> {
self.ppf(1.0 - p)
}
fn interval(&self, alpha: f64) -> StatsResult<(f64, f64)> {
let q = (1.0 - alpha) / 2.0;
let lower = self.ppf(q)?;
let upper = self.ppf(1.0 - q)?;
Ok((lower, upper))
}
fn pdf_tensor<R: Runtime<DType = DType>, C>(
&self,
x: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + RuntimeClient<R>;
fn log_pdf_tensor<R: Runtime<DType = DType>, C>(
&self,
x: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + RuntimeClient<R>;
fn cdf_tensor<R: Runtime<DType = DType>, C>(
&self,
x: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
fn sf_tensor<R: Runtime<DType = DType>, C>(
&self,
x: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
fn log_cdf_tensor<R: Runtime<DType = DType>, C>(
&self,
x: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
fn ppf_tensor<R: Runtime<DType = DType>, C>(
&self,
p: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
fn isf_tensor<R: Runtime<DType = DType>, C>(
&self,
p: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
}
pub trait DiscreteDistribution: Distribution {
fn pmf(&self, k: u64) -> f64;
fn log_pmf(&self, k: u64) -> f64 {
self.pmf(k).ln()
}
fn cdf(&self, k: u64) -> f64;
fn sf(&self, k: u64) -> f64 {
1.0 - self.cdf(k)
}
fn ppf(&self, p: f64) -> StatsResult<u64>;
fn isf(&self, p: f64) -> StatsResult<u64> {
self.ppf(1.0 - p)
}
fn pmf_tensor<R: Runtime<DType = DType>, C>(
&self,
k: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
fn log_pmf_tensor<R: Runtime<DType = DType>, C>(
&self,
k: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
fn cdf_tensor<R: Runtime<DType = DType>, C>(
&self,
k: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
fn sf_tensor<R: Runtime<DType = DType>, C>(
&self,
k: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
fn ppf_tensor<R: Runtime<DType = DType>, C>(
&self,
p: &Tensor<R>,
client: &C,
) -> Result<Tensor<R>>
where
C: TensorOps<R> + ScalarOps<R> + SpecialFunctions<R> + RuntimeClient<R>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_distribution_trait_bounds() {
fn _check_continuous_bounds<D: ContinuousDistribution>(d: &D, x: f64) -> f64 {
d.pdf(x) + d.cdf(x) + d.sf(x)
}
fn _check_discrete_bounds<D: DiscreteDistribution>(d: &D, k: u64) -> f64 {
d.pmf(k) + d.cdf(k) + d.sf(k)
}
}
}