r2rs-nmath 0.1.1

Statistics programming for Rust based on R's nmath package
Documentation
// Translation of nmath's plogis
//
// Copyright (C) 2020 neek-sss
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use strafe_type::{FloatConstraint, LogProbability64, Probability64, Rational64, Real64};

use crate::traits::DPQ;

/// Compute $ log(1 + exp(x)) $  without overflow (and fast for $ x > 18 $)
/// For the two cutoffs, consider in R
/// curve(x.exp().ln_1p() - x,       33.1, 33.5, n=2^10)
/// curve(x+(-x).exp() - x.exp().ln_1p(), 15, 25,   n=2^11)
pub fn log1pexp(x: f64) -> f64 {
    if x <= 18.0 {
        return x.exp().ln_1p();
    }
    if x > 33.3 {
        return x;
    }
    // else: 18.0 < x <= 33.3 :
    x + (-x).exp()
}

pub fn plogis<RE1: Into<Real64>, RE2: Into<Real64>, RA: Into<Rational64>>(
    x: RE1,
    location: RE2,
    scale: RA,
    lower_tail: bool,
) -> Probability64 {
    plogis_inner(x, location, scale, lower_tail, false).into()
}

pub fn log_plogis<RE1: Into<Real64>, RE2: Into<Real64>, RA: Into<Rational64>>(
    x: RE1,
    location: RE2,
    scale: RA,
    lower_tail: bool,
) -> LogProbability64 {
    plogis_inner(x, location, scale, lower_tail, true).into()
}

fn plogis_inner<RE1: Into<Real64>, RE2: Into<Real64>, RA: Into<Rational64>>(
    x: RE1,
    location: RE2,
    scale: RA,
    lower_tail: bool,
    log: bool,
) -> f64 {
    let mut x = x.into().unwrap();
    let location = location.into().unwrap();
    let scale = scale.into().unwrap();

    x = (x - location) / scale;

    if let Some(ret) = x.p_bounds_inf_01(lower_tail, log) {
        return ret;
    }

    if log {
        // log(1 / (1 + ( +- x ).exp())) = -log(1 + ( +- x).exp())
        -log1pexp(if lower_tail { -x } else { x })
    } else {
        1.0 / (1.0 + (if lower_tail { -x } else { x }).exp())
    }
}