Skip to content

Commit 06a21d2

Browse files
authored
Merge pull request #60 from jorenham/orthopoly-api-consistency
2 parents b7eab7c + 45690b8 commit 06a21d2

File tree

7 files changed

+262
-113
lines changed

7 files changed

+262
-113
lines changed

src/ffi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ unsafe extern "C" {
137137
pub(crate) fn math_gegenbauer_derivative(n: c_uint, lambda: f64, x: f64, k: c_uint) -> f64;
138138

139139
// boost/math/special_functions/hermite.hpp
140+
#[cfg(test)]
140141
pub(crate) fn math_hermite(n: c_uint, x: f64) -> f64;
141142

142143
// boost/math/special_functions/heuman_lambda.hpp

src/math/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,12 @@
9797
//! - [`laguerre_assoc`]
9898
//! - [`laguerre_assoc_next`]
9999
//! - Hermite Polynomials
100-
//! - [`hermite`]
101-
//! - [`hermite_next`]
100+
//! - [`hermite_h`]
101+
//! - [`hermite_h_derivative`]
102+
//! - [`hermite_h_next`]
103+
//! - [`hermite_he`]
104+
//! - [`hermite_he_derivative`]
105+
//! - [`hermite_he_next`]
102106
//! - Gegenbauer Polynomials
103107
//! - [`gegenbauer`]
104108
//! - [`gegenbauer_derivative`]

src/math/special_functions/chebyshev.rs

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,39 @@
22
33
use crate::ffi;
44

5+
/// Chebyshev polynomial of the 1st kind T<sub>n</sub>(x).
6+
///
7+
/// Defined as <i>T<sub>n</sub>(</i>cos<i>(θ)) = </i>cos<i>(n θ)</i>.
8+
///
9+
/// See [`chebyshev_t_prime`] for the derivative, and [`chebyshev_u`] for the second kind.
10+
///
11+
/// Corresponds to `boost::math::chebyshev_t` in C++.
12+
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/chebyshev.html>
13+
pub fn chebyshev_t(n: u32, x: f64) -> f64 {
14+
unsafe { ffi::math_chebyshev_t(n, x) }
15+
}
16+
17+
/// Derivative of [`chebyshev_t`].
18+
///
19+
/// Corresponds to `boost::math::chebyshev_t_prime` in C++.
20+
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/chebyshev.html>
21+
#[doc(alias = "chebyshev_t_derivative")]
22+
pub fn chebyshev_t_prime(n: u32, x: f64) -> f64 {
23+
unsafe { ffi::math_chebyshev_t_prime(n, x) }
24+
}
25+
26+
/// Chebyshev polynomial of the 2nd kind U<sub>n</sub>(x).
27+
///
28+
/// Defined as <i>U<sub>n</sub>(</i>cos<i>(θ)) = </i>sin<i>((n+1) θ) / </i>sin<i>(θ)</i>.
29+
///
30+
/// See [`chebyshev_t`] for the first kind.
31+
///
32+
/// Corresponds to `boost::math::chebyshev_u` in C++.
33+
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/chebyshev.html>
34+
pub fn chebyshev_u(n: u32, x: f64) -> f64 {
35+
unsafe { ffi::math_chebyshev_u(n, x) }
36+
}
37+
538
/// Recurrence relation for Chebyshev polynomials
639
///
740
/// *T<sub>n+1</sub>(x) = 2 x T<sub>n</sub>(x) - T<sub>n-1</sub>(x)*
@@ -20,8 +53,8 @@ use crate::ffi;
2053
/// let t1 = chebyshev_t(1, x); // x
2154
/// let t2 = chebyshev_t(2, x); // 2x² - 1
2255
/// let t3 = chebyshev_t(3, x); // 4x³ - 3x
23-
/// assert_relative_eq!(chebyshev_next(&x, &t1, &t0), t2);
24-
/// assert_relative_eq!(chebyshev_next(&x, &t2, &t1), t3);
56+
/// assert_relative_eq!(chebyshev_next(x, t1, t0), t2);
57+
/// assert_relative_eq!(chebyshev_next(x, t2, t1), t3);
2558
/// ```
2659
///
2760
/// [`chebyshev_u`] recurrence:
@@ -34,46 +67,15 @@ use crate::ffi;
3467
/// let u1 = chebyshev_u(1, x); // 2x
3568
/// let u2 = chebyshev_u(2, x); // 4x² - 1
3669
/// let u3 = chebyshev_u(3, x); // 8x³ - 4x
37-
/// assert_relative_eq!(chebyshev_next(&x, &u1, &u0), u2);
38-
/// assert_relative_eq!(chebyshev_next(&x, &u2, &u1), u3);
70+
/// assert_relative_eq!(chebyshev_next(x, u1, u0), u2);
71+
/// assert_relative_eq!(chebyshev_next(x, u2, u1), u3);
3972
/// ```
40-
#[allow(non_snake_case)]
4173
#[inline(always)]
42-
pub fn chebyshev_next(x: &f64, Tn: &f64, Tn_1: &f64) -> f64 {
43-
2.0 * x * Tn - Tn_1
44-
}
45-
46-
/// Chebyshev polynomial of the 1st kind T<sub>n</sub>(x).
47-
///
48-
/// Defined as <i>T<sub>n</sub>(</i>cos<i>(θ)) = </i>cos<i>(n θ)</i>.
49-
///
50-
/// See [`chebyshev_t_prime`] for the derivative, and [`chebyshev_u`] for the second kind.
51-
///
52-
/// Corresponds to `boost::math::chebyshev_t` in C++.
53-
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/chebyshev.html>
54-
pub fn chebyshev_t(n: u32, x: f64) -> f64 {
55-
unsafe { ffi::math_chebyshev_t(n, x) }
56-
}
57-
58-
/// Derivative of [`chebyshev_t`].
59-
///
60-
/// Corresponds to `boost::math::chebyshev_t_prime` in C++.
61-
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/chebyshev.html>
62-
#[doc(alias = "chebyshev_t_derivative")]
63-
pub fn chebyshev_t_prime(n: u32, x: f64) -> f64 {
64-
unsafe { ffi::math_chebyshev_t_prime(n, x) }
65-
}
66-
67-
/// Chebyshev polynomial of the 2nd kind U<sub>n</sub>(x).
68-
///
69-
/// Defined as <i>U<sub>n</sub>(</i>cos<i>(θ)) = </i>sin<i>((n+1) θ) / </i>sin<i>(θ)</i>.
70-
///
71-
/// See [`chebyshev_t`] for the first kind.
72-
///
73-
/// Corresponds to `boost::math::chebyshev_u` in C++.
74-
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/chebyshev.html>
75-
pub fn chebyshev_u(n: u32, x: f64) -> f64 {
76-
unsafe { ffi::math_chebyshev_u(n, x) }
74+
#[allow(non_snake_case)]
75+
#[doc(alias = "chebyshev_t_next")]
76+
#[doc(alias = "chebyshev_u_next")]
77+
pub fn chebyshev_next(x: f64, Tn: f64, Tn_prev: f64) -> f64 {
78+
2.0 * x * Tn - Tn_prev
7779
}
7880

7981
#[cfg(test)]
Lines changed: 177 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,202 @@
11
//! boost/math/special_functions/hermite.hpp
22
3-
use crate::ffi;
4-
use core::ffi::c_uint;
3+
/// Recurrence relation for [`hermite_h`]
4+
///
5+
/// *H<sub>n+1</sub>(x) = 2xH<sub>n</sub>(x) - 2nH<sub>n-1</sub>(x)*
6+
///
7+
/// # Examples
8+
///
9+
/// ```
10+
/// # use boost::math::{hermite_h, hermite_h_next};
11+
/// let x = 0.42;
12+
/// let p0 = hermite_h(0, x); // 1
13+
/// let p1 = hermite_h(1, x); // 2x
14+
/// let p2 = hermite_h(2, x); // 4x² - 2
15+
/// let p3 = hermite_h(3, x); // 8x³ - 12x
16+
/// assert_eq!(hermite_h_next(1, x, p1, p0), p2);
17+
/// assert_eq!(hermite_h_next(2, x, p2, p1), p3);
18+
/// ```
19+
#[inline(always)]
20+
pub fn hermite_h_next(n: u32, x: f64, pn: f64, pn_prev: f64) -> f64 {
21+
2.0 * hermite_he_next(n, x, pn, pn_prev)
22+
}
523

624
/// Hermite Polynomial *H<sub>n</sub>(x)*
725
///
8-
/// Corresponds to `boost::math::hermite(n, x)`.
26+
/// Note that this is the "physicist's" Hermite polynomial.
27+
///
28+
/// This is a pure rust implementation equivalent to the `boost::math::hermite(n, x)` C++ function.
929
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/hermite.html>
10-
pub fn hermite(n: u32, x: f64) -> f64 {
11-
unsafe { ffi::math_hermite(n as c_uint, x) }
30+
pub fn hermite_h(n: u32, x: f64) -> f64 {
31+
// Implement Hermite polynomials via recurrence:
32+
let (mut p0, mut p1) = (1.0, 2.0 * x);
33+
if n == 0 {
34+
p0
35+
} else {
36+
for c in 1..n {
37+
(p0, p1) = (p1, hermite_h_next(c, x, p1, p0));
38+
}
39+
p1
40+
}
1241
}
1342

14-
/// Recurrence relation for [`hermite`]
43+
/// *k*-th derivative of the Hermite polynomial *H<sub>n</sub>(x)*
1544
///
16-
/// *H<sub>n+1</sub>(x) = 2xH<sub>n</sub>(x) - 2nH<sub>n-1</sub>(x)*
45+
/// This function does not exist in the Boost Math C++ library.
46+
#[doc(alias = "hermite_h_prime")]
47+
pub fn hermite_h_derivative(n: u32, x: f64, k: u32) -> f64 {
48+
if n < k {
49+
0.0
50+
} else {
51+
let mut p = hermite_h(n - k, x);
52+
for m in 0..k {
53+
p *= 2.0 * (n - m) as f64
54+
}
55+
p
56+
}
57+
}
58+
59+
/// Recurrence relation for [`hermite_he`]
60+
///
61+
/// *He<sub>n+1</sub>(x) = x He<sub>n</sub>(x) - n He<sub>n-1</sub>(x)*
1762
///
1863
/// # Examples
1964
///
2065
/// ```
21-
/// # use boost::math::{hermite, hermite_next};
66+
/// # use boost::math::{hermite_he, hermite_he_next};
2267
/// let x = 0.42;
23-
/// let h0 = hermite(0, x); // 1
24-
/// let h1 = hermite(1, x); // 2x
25-
/// let h2 = hermite(2, x); // 4x² - 2
26-
/// let h3 = hermite(3, x); // 8x³ - 12x
27-
/// assert_eq!(hermite_next(1, &x, &h1, &h0), h2);
28-
/// assert_eq!(hermite_next(2, &x, &h2, &h1), h3);
68+
/// let p0 = hermite_he(0, x); // 1
69+
/// let p1 = hermite_he(1, x); // x
70+
/// let p2 = hermite_he(2, x); // x² - 1
71+
/// let p3 = hermite_he(3, x); // x³ - 3x
72+
/// assert_eq!(hermite_he_next(1, x, p1, p0), p2);
73+
/// assert_eq!(hermite_he_next(2, x, p2, p1), p3);
2974
/// ```
30-
#[allow(non_snake_case)]
3175
#[inline(always)]
32-
pub fn hermite_next(n: u32, x: &f64, Hn: &f64, Hn_1: &f64) -> f64 {
33-
2.0 * (x * Hn - (n as f64) * Hn_1)
76+
pub fn hermite_he_next(n: u32, x: f64, pn: f64, pn_prev: f64) -> f64 {
77+
x * pn - (n as f64) * pn_prev
78+
}
79+
80+
/// Monic Hermite Polynomial *He<sub>n</sub>(x)*
81+
///
82+
/// Note that this is the "probabilist's" Hermite polynomial, which is monic (leading coefficient
83+
/// is 1). It is related to the "physicist's" Hermite polynomial ([`hermite_h`]) by
84+
///
85+
/// *He<sub>n</sub>(x) = 2<sup>-n/2</sup> H<sub>n</sub>(x / √2)*
86+
///
87+
/// This function does not exist in the Boost Math C++ library.
88+
pub fn hermite_he(n: u32, x: f64) -> f64 {
89+
let (mut p0, mut p1) = (1.0, x);
90+
if n == 0 {
91+
p0
92+
} else {
93+
for c in 1..n {
94+
(p0, p1) = (p1, hermite_he_next(c, x, p1, p0));
95+
}
96+
p1
97+
}
98+
}
99+
100+
/// *k*-th derivative of the Hermite polynomial *He<sub>n</sub>(x)*
101+
///
102+
/// This function does not exist in the Boost Math C++ library.
103+
#[doc(alias = "hermite_he_prime")]
104+
pub fn hermite_he_derivative(n: u32, x: f64, k: u32) -> f64 {
105+
if n < k {
106+
0.0
107+
} else {
108+
let mut p = hermite_he(n - k, x);
109+
for m in 0..k {
110+
p *= (n - m) as f64
111+
}
112+
p
113+
}
34114
}
35115

36116
#[cfg(test)]
37117
mod tests {
38118
use super::*;
119+
use core::f64::consts::FRAC_1_SQRT_2;
120+
121+
const ATOL: f64 = 1e-15;
122+
const RTOL: f64 = 1e-12;
123+
124+
fn hermite_h_ffi(n: u32, x: f64) -> f64 {
125+
unsafe { crate::ffi::math_hermite(n as core::ffi::c_uint, x) }
126+
}
127+
128+
#[test]
129+
fn test_hermite_h() {
130+
for n in 0..20 {
131+
for x in -10..=10 {
132+
let x = x as f64 * 0.1;
133+
let h = hermite_h_ffi(n, x);
134+
assert_relative_eq!(hermite_h(n, x), h, epsilon = ATOL, max_relative = RTOL);
135+
}
136+
}
137+
}
138+
139+
#[test]
140+
fn test_hermite_he() {
141+
for n in 0..20 {
142+
let c = FRAC_1_SQRT_2.powi(n as i32);
143+
for x in -10..=10 {
144+
let x = x as f64 * 0.1;
145+
let h = c * hermite_h(n, FRAC_1_SQRT_2 * x);
146+
assert_relative_eq!(hermite_he(n, x), h, epsilon = ATOL, max_relative = RTOL);
147+
}
148+
}
149+
}
39150

40151
#[test]
41-
fn test_hermite() {
42-
assert_relative_eq!(hermite(0, 1.0), 1.0);
43-
assert_relative_eq!(hermite(1, 1.0), 2.0);
44-
assert_relative_eq!(hermite(2, 1.0), 2.0);
45-
assert_relative_eq!(hermite(3, 1.0), -4.0);
46-
assert_relative_eq!(hermite(4, 1.0), -20.0);
152+
fn test_hermite_h_derivative() {
153+
for n in 0..20 {
154+
for x in -10..=10 {
155+
let x = x as f64 * 0.1;
156+
157+
let d0 = hermite_h_derivative(n, x, 0);
158+
assert_relative_eq!(d0, hermite_h(n, x));
159+
160+
let d1 = hermite_h_derivative(n, x, 1);
161+
if n >= 1 {
162+
assert_relative_eq!(d1, (2 * n) as f64 * hermite_h(n - 1, x));
163+
} else {
164+
assert_eq!(d1, 0.0)
165+
}
166+
167+
let d2 = hermite_h_derivative(n, x, 2);
168+
if n >= 2 {
169+
assert_relative_eq!(d2, (4 * n * (n - 1)) as f64 * hermite_h(n - 2, x));
170+
} else {
171+
assert_eq!(d2, 0.0)
172+
}
173+
}
174+
}
175+
}
176+
177+
#[test]
178+
fn test_hermite_he_derivative() {
179+
for n in 0..20 {
180+
for x in -10..=10 {
181+
let x = x as f64 * 0.1;
182+
183+
let d0 = hermite_he_derivative(n, x, 0);
184+
assert_relative_eq!(d0, hermite_he(n, x));
185+
186+
let d1 = hermite_he_derivative(n, x, 1);
187+
if n >= 1 {
188+
assert_relative_eq!(d1, n as f64 * hermite_he(n - 1, x));
189+
} else {
190+
assert_eq!(d1, 0.0)
191+
}
192+
193+
let d2 = hermite_he_derivative(n, x, 2);
194+
if n >= 2 {
195+
assert_relative_eq!(d2, (n * (n - 1)) as f64 * hermite_he(n - 2, x));
196+
} else {
197+
assert_eq!(d2, 0.0)
198+
}
199+
}
200+
}
47201
}
48202
}

src/math/special_functions/jacobi.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
use crate::ffi;
44
use core::ffi::c_uint;
55

6-
/// Jacobi Polynomial *P<sub>n</sub><sup>(α, β)</sup>(x)*
7-
///
8-
/// Corresponds to `boost::math::jacobi(n, alpha, beta, x)`.
6+
/// Jacobi Polynomial *P<sub>n</sub><sup>(α,β)</sup>(x)*
97
///
8+
/// Corresponds to `boost::math::jacobi(n, alpha, beta, x)` in C++.
109
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/jacobi.html>
1110
pub fn jacobi(n: u32, alpha: f64, beta: f64, x: f64) -> f64 {
1211
unsafe { ffi::math_jacobi(n as c_uint, alpha, beta, x) }
1312
}
1413

1514
/// *k*-th derivative of [`jacobi`] with respect to `x`
1615
///
17-
/// Corresponds to `boost::math::jacobi_derivative(n, alpha, beta, x, k)`.
18-
///
16+
/// Corresponds to `boost::math::jacobi_derivative(n, alpha, beta, x, k)` in C++.
1917
/// <https://boost.org/doc/libs/latest/libs/math/doc/html/math_toolkit/sf_poly/jacobi.html>
18+
#[doc(alias = "jacobi_prime")]
19+
#[doc(alias = "jacobi_double_prime")]
2020
pub fn jacobi_derivative(n: u32, alpha: f64, beta: f64, x: f64, k: u32) -> f64 {
2121
unsafe { ffi::math_jacobi_derivative(n as c_uint, alpha, beta, x, k as c_uint) }
2222
}

0 commit comments

Comments
 (0)