Skip to content

Commit

Permalink
Fully replace cephes special functions with new implementation
Browse files Browse the repository at this point in the history
We can compute the p-values using the cdf for a Student's T
distribution with location 0 and scale 1.0.

This lets us replace the `stdtr` function that was originally
ported from the cephes library with an implementation
adapted from the statrs library, that is well documented
and doesn't trigger clippy's many_single_char_names lint.
  • Loading branch information
n1m3 committed Jul 12, 2022
1 parent 87c864f commit b8faa71
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 392 deletions.
33 changes: 28 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,10 @@ use std::iter;

use nalgebra::{DMatrix, DVector, RowDVector};

pub use error::{Error, InconsistentSlopes};
use special_functions::stdtr;
pub use crate::error::{Error, InconsistentSlopes};
use crate::stats::students_t_cdf;

mod error;
mod special_functions;
mod stats;

macro_rules! ensure {
Expand Down Expand Up @@ -143,6 +142,24 @@ pub fn slices_almost_equal(a: &[f64], b: &[f64], precision: f64) -> bool {
true
}

/// Compares `a` and `b` approximately.
///
/// They are considered equal if
/// `(a-b).abs() <= epsilon` or they differ my at most `max_ulps`
/// `unit of least precision` i.e. there are at most `max_ulps`
/// other representable floating point numbers between `a` and `b`
fn ulps_eq(a: f64, b: f64, epsilon: f64, max_ulps: u32) -> bool {
if (a - b).abs() <= epsilon {
return true;
}
if a.signum() != b.signum() {
return false;
}
let a: u64 = a.to_bits();
let b: u64 = b.to_bits();
a.abs_diff(b) <= max_ulps as u64
}

/// A builder to create and fit a linear regression model.
///
/// Given a dataset and a set of columns to use this builder
Expand Down Expand Up @@ -921,8 +938,14 @@ impl LowLevelRegressionModel {
let pvalues: Vec<_> = tvalues
.iter()
.cloned()
.map(|x| stdtr(df_resid as i64, -(x.abs())) * 2.)
.collect();
.map(|x| students_t_cdf(-(x.abs()), df_resid as i64).map(|i| i * 2.))
.collect::<Option<_>>()
.ok_or_else(|| {
Error::ModelFittingError(
"Failed to calculate p-values: students_t_cdf failed due to invalid parameters"
.into(),
)
})?;
// Convert these from interal Matrix types to user facing types
let parameters: Vec<f64> = parameters.iter().copied().collect();
let se: Vec<f64> = se.iter().copied().collect();
Expand Down
Loading

0 comments on commit b8faa71

Please sign in to comment.