Trait lnexp::LnExp

source ·
pub trait LnExp {
    // Required methods
    fn ln_1m_exp(&self) -> Self;
    fn ln_1p_exp(&self) -> Self;
    fn ln_exp_m1(&self) -> Self;
    fn logit(&self) -> Self;
    fn inv_logit(&self) -> Self;
    fn ln_inv_logit(&self) -> Self;
    fn logit_exp(&self) -> Self;
    fn ln_1m_inv_logit(&self) -> Self;
    fn logit_1m_exp(&self) -> Self;
}
Expand description

The LnExp trait extends the functionality afforded by a libm, providing careful evaluation of compositions of the ln, exp, ln_1p and exp_m1 functions so as to avoid underflow/overflow. Furthermore, in some cases, the methods provided by this trait result in fewer calls than the equivalent function composition.

Required Methods§

source

fn ln_1m_exp(&self) -> Self

Returns ln(1 - exp(x)), computed as described in Martin Maechler (2012), Accurately Computing log(1 − exp(− |a|)). For x > 0, the result is undefined, hence, the return value is nan.

Examples
use lnexp::LnExp;

let x: f64 = -1e-20;
assert!(x.ln_1m_exp().is_finite());
// compare with the naive computation
assert!((-x.exp()).ln_1p().is_infinite());

let x: f64 = -50.0;
assert_ne!(x.ln_1m_exp(), 0.0);
// the result if we blindly applied `ln(-exp_m1(x))` to the entire range
assert_eq!((-x.exp_m1()).ln(), 0.0);

// An interesting property: the inverse of `ln_1m_exp` is `ln_1m_exp`.
let x: f64 = -2.0;
assert_eq!(x.ln_1m_exp().ln_1m_exp(), x);
source

fn ln_1p_exp(&self) -> Self

Returns ln(1 + exp(x)), computed as described in Maechler (2012), for which a theoretical basis has been provided by cossio (2022), untitled. The inverse of this function is ln_exp_m1.

Examples
use lnexp::LnExp;

let x: f64 = (1023.0 * (2.0_f64).ln()) + 1.0;
assert!(x.ln_1p_exp().is_finite());
// compare with naive computation
assert_eq!(x.exp().ln_1p(), f64::INFINITY);
source

fn ln_exp_m1(&self) -> Self

Returns ln(exp(x) - 1), the inverse of ln_1p_exp, computed by inverting the case analysis described in Maechler (2012).

Examples
use lnexp::LnExp;

let x: f64 = (1023.0 * (2.0_f64).ln()) + 1.0;
assert!(x.ln_exp_m1().is_finite());
// compare with naive computation
assert_eq!(x.exp_m1().ln(), f64::INFINITY);
source

fn logit(&self) -> Self

Returns the logit, mapping from the closed interval [0,1] to a real number.

Examples
use lnexp::LnExp;

let x: f64 = 1.0;
assert_eq!(x.logit(), f64::INFINITY);

let x: f64 = 0.5;
assert_eq!(x.logit(), 0.0);

let x: f64 = 0.0;
assert_eq!(x.logit(), f64::NEG_INFINITY);
source

fn inv_logit(&self) -> Self

Returns the inverse-logit mapping from a real number to the closed interval [0,1]. This is the inverse of logit.

Examples
use lnexp::LnExp;

let x: f64 = f64::INFINITY;
assert_eq!(x.inv_logit(), 1.0);

let x: f64 = 0.0;
assert_eq!(x.inv_logit(), 0.5);

let x: f64 = f64::NEG_INFINITY;
assert_eq!(x.inv_logit(), 0.0);

// Smooth lower bound by enforcing that values less than
// `logit(5.0e-324)` (`logit(1.0e-45)` if `f32`) map to 0.0
// when the inverse-logit is applied.
let x: f64 = -745.0; // Slightly less than lower bound
assert_eq!(x.inv_logit(), 0.0);
// compare with naive computation
let e = x.exp();
assert_ne!(e / (1.0 + e), 0.0);

// Smooth upper bound by enforcing that values greater than
// `logit(1.0 - epsilon / 2.0)` map to 1.0 when the inverse-logit is applied.
let x: f64 = 36.8; // Slightly larger than upper bound
assert_eq!(x.inv_logit(), 1.0);
// compare with naive computation
let e = x.exp();
assert_ne!(e / (1.0 + e), 1.0);
source

fn ln_inv_logit(&self) -> Self

Returns the natural logarithm of the inverse-logit function, computed more carefully than the composition of functions x.inv_logit().ln(). This utilizes the identity ln(inv_logit(x)) = -ln(1 + exp(-x)) to compute the result via ln_1p_exp.

Examples
use lnexp::LnExp;

let x: f64 = -745.0;
assert!(x.ln_inv_logit().is_finite());
// compare with naive computation
assert_eq!(x.inv_logit().ln(), f64::NEG_INFINITY);
source

fn logit_exp(&self) -> Self

Returns the logit of the exponential of self, i.e. logit(exp(x)), computed more carefully and with fewer function calls than the composition. This is the inverse of ln_inv_logit.

Examples
use lnexp::LnExp;

let x: f64 = 50.0;
assert_eq!(x.ln_inv_logit().logit_exp(), x);
let x: f64 = 743.0;
assert!(x.ln_inv_logit().logit_exp().is_finite());
// compare with naive computation
assert_eq!(x.ln_inv_logit().exp().logit(), f64::INFINITY);
source

fn ln_1m_inv_logit(&self) -> Self

Returns the natural logarithm of the 1 minus the inverse logit function, computed more carefully and with fewer function calls than the composition of functions (1.0 - x.inv_logit()).ln(). This exploits negation in the log-odds domain to compute the result via ln_1p_exp,

Examples
use lnexp::LnExp;

let x: f64 = 50.0;
assert!(x.ln_1m_inv_logit().is_finite());
// compare with naive computation
assert_eq!((1.0 - x.inv_logit()).ln(), f64::NEG_INFINITY);
source

fn logit_1m_exp(&self) -> Self

Returns the logit of 1 minus the exponential of self, i.e. logit(1 - exp(x)), computed more carefully and with fewer function calls than the composition. This is the inverse of ln_1m_inv_logit.

Examples
use lnexp::LnExp;

let x: f64 = 50.0;
assert_eq!(x.ln_1m_inv_logit().logit_1m_exp(), x);
let x: f64 = 743.0;
assert!(x.ln_1m_inv_logit().is_finite());
// compare with naive computation
assert_eq!((1.0 - x.ln_1m_inv_logit().exp()).logit(), f64::INFINITY);

Implementations on Foreign Types§

source§

impl LnExp for f32

source§

fn ln_1m_exp(&self) -> f32

source§

fn ln_1p_exp(&self) -> f32

source§

fn ln_exp_m1(&self) -> f32

source§

fn logit(&self) -> f32

source§

fn inv_logit(&self) -> f32

source§

fn ln_inv_logit(&self) -> f32

source§

fn logit_exp(&self) -> f32

source§

fn ln_1m_inv_logit(&self) -> f32

source§

fn logit_1m_exp(&self) -> f32

source§

impl LnExp for f64

source§

fn ln_1m_exp(&self) -> f64

source§

fn ln_1p_exp(&self) -> f64

source§

fn ln_exp_m1(&self) -> f64

source§

fn logit(&self) -> f64

source§

fn inv_logit(&self) -> f64

source§

fn ln_inv_logit(&self) -> f64

source§

fn logit_exp(&self) -> f64

source§

fn ln_1m_inv_logit(&self) -> f64

source§

fn logit_1m_exp(&self) -> f64

Implementors§