pxfm/gamma/trigammaf.rs
1/*
2 * // Copyright (c) Radzivon Bartoshyk 8/2025. All rights reserved.
3 * //
4 * // Redistribution and use in source and binary forms, with or without modification,
5 * // are permitted provided that the following conditions are met:
6 * //
7 * // 1. Redistributions of source code must retain the above copyright notice, this
8 * // list of conditions and the following disclaimer.
9 * //
10 * // 2. Redistributions in binary form must reproduce the above copyright notice,
11 * // this list of conditions and the following disclaimer in the documentation
12 * // and/or other materials provided with the distribution.
13 * //
14 * // 3. Neither the name of the copyright holder nor the names of its
15 * // contributors may be used to endorse or promote products derived from
16 * // this software without specific prior written permission.
17 * //
18 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use crate::common::{f_fmla, is_integerf};
30use crate::polyeval::{f_polyeval6, f_polyeval10};
31use crate::sin_cosf::fast_sinpif;
32
33#[inline]
34fn approx_trigamma(x: f64) -> f64 {
35 if x <= 1. {
36 // Polynomial for Trigamma[x+1]
37 // <<FunctionApproximations`
38 // ClearAll["Global`*"]
39 // f[x_]:=PolyGamma[1, x+1]
40 // {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{-0.99999999,0},9,9},WorkingPrecision->75,MaxIterations->100]
41 // num=Numerator[approx];
42 // den=Denominator[approx];
43 // poly=num;
44 // coeffs=CoefficientList[poly,z];
45 // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
46 // poly=den;
47 // coeffs=CoefficientList[poly,z];
48 // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
49 let p_num = f_polyeval10(
50 x,
51 f64::from_bits(0x3ffa51a6625307d3),
52 f64::from_bits(0x40142fc9c4f02b3f),
53 f64::from_bits(0x401b30a762805b44),
54 f64::from_bits(0x4014dc84c95656bd),
55 f64::from_bits(0x4003e44f4c820b4c),
56 f64::from_bits(0x3fe81f37523197d3),
57 f64::from_bits(0x3fc22bffe2490221),
58 f64::from_bits(0x3f8f221a6329ea36),
59 f64::from_bits(0x3f47406930b9563c),
60 f64::from_bits(0xbd99cd44c6ad497a),
61 );
62 let p_den = f_polyeval10(
63 x,
64 f64::from_bits(0x3ff0000000000000),
65 f64::from_bits(0x40121e3db4e0a2f3),
66 f64::from_bits(0x40218e97a5430c4f),
67 f64::from_bits(0x402329897737b159),
68 f64::from_bits(0x401a0fdc27807c2d),
69 f64::from_bits(0x4006ff242e1f3a51),
70 f64::from_bits(0x3fea6eda129c4e85),
71 f64::from_bits(0x3fc32700b2ae2e88),
72 f64::from_bits(0x3f8fdc1dc6116d41),
73 f64::from_bits(0x3f4740690261cfbc),
74 );
75 return p_num / p_den + 1. / (x * x);
76 } else if x <= 4. {
77 // Polynomial for Trigamma[x+1]
78 // <<FunctionApproximations`
79 // ClearAll["Global`*"]
80 // f[x_]:=PolyGamma[1, x+1]
81 // {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{0,3},9,9},WorkingPrecision->75,MaxIterations->100]
82 // num=Numerator[approx];
83 // den=Denominator[approx];
84 // poly=num;
85 // coeffs=CoefficientList[poly,z];
86 // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
87 // poly=den;
88 // coeffs=CoefficientList[poly,z];
89 // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
90 let p_num = f_polyeval10(
91 x,
92 f64::from_bits(0x3ffa51a6625307d3),
93 f64::from_bits(0x4015167fa2d2b5a8),
94 f64::from_bits(0x401dd40865d5985e),
95 f64::from_bits(0x4018353d9425fb58),
96 f64::from_bits(0x4008a12aa45851fa),
97 f64::from_bits(0x3ff018736d0c5dbe),
98 f64::from_bits(0x3fca715702bdb519),
99 f64::from_bits(0x3f9908a9d73d983c),
100 f64::from_bits(0x3f54fd9cbbb46314),
101 f64::from_bits(0xbd00b8a28c578ab5),
102 );
103 let p_den = f_polyeval10(
104 x,
105 f64::from_bits(0x3ff0000000000000),
106 f64::from_bits(0x4012aa7f041a768b),
107 f64::from_bits(0x4022c2604e5f9c7a),
108 f64::from_bits(0x4025655b63c2db22),
109 f64::from_bits(0x401eaa8e59c8295d),
110 f64::from_bits(0x400cc8724a58809c),
111 f64::from_bits(0x3ff1c7a91c8e3c40),
112 f64::from_bits(0x3fcc05613a11183e),
113 f64::from_bits(0x3f99b096bd3ce542),
114 f64::from_bits(0x3f54fd9cbb9c6167),
115 );
116 return p_num / p_den + 1. / (x * x);
117 } else if x <= 10. {
118 // Polynomial for Trigamma[x+1]
119 // <<FunctionApproximations`
120 // ClearAll["Global`*"]
121 // f[x_]:=PolyGamma[1, x+1]
122 // {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{3,9},9,9},WorkingPrecision->75,MaxIterations->100]
123 // num=Numerator[approx];
124 // den=Denominator[approx];
125 // poly=num;
126 // coeffs=CoefficientList[poly,z];
127 // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
128 // poly=den;
129 // coeffs=CoefficientList[poly,z];
130 // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
131 let p_num = f_polyeval10(
132 x,
133 f64::from_bits(0x3ffa51a664b1b211),
134 f64::from_bits(0x4016d7f75881312a),
135 f64::from_bits(0x4021b10defb47bcc),
136 f64::from_bits(0x401fe9633665e1bf),
137 f64::from_bits(0x4012601cce6766d7),
138 f64::from_bits(0x3ffbd0ece1c435f1),
139 f64::from_bits(0x3fdb3fd0e233c485),
140 f64::from_bits(0x3faffdedea90b870),
141 f64::from_bits(0x3f71a4bbf0d00147),
142 f64::from_bits(0xbc0aae286498a357),
143 );
144 let p_den = f_polyeval10(
145 x,
146 f64::from_bits(0x3ff0000000000000),
147 f64::from_bits(0x4013bbbd604d685d),
148 f64::from_bits(0x40253a4fca05438e),
149 f64::from_bits(0x402a4aba4634f14f),
150 f64::from_bits(0x4024cdd23bd6284a),
151 f64::from_bits(0x4015fbe371275c3f),
152 f64::from_bits(0x3fff4d7ebf7d1ed0),
153 f64::from_bits(0x3fdd459154d7bc72),
154 f64::from_bits(0x3fb08c1cd4cedca3),
155 f64::from_bits(0x3f71a4bbf0d0012d),
156 );
157 return p_num / p_den + 1. / (x * x);
158 }
159 // asymptotic expansion Trigamma[x] = 1/x + 1/x^2 + sum(Bernoulli(2*k)/x^(2*k + 1))
160 // Generated in SageMath:
161 // var('x')
162 // def bernoulli_terms(x, N):
163 // S = 0
164 // for k in range(1, N+1):
165 // B = bernoulli(2*k)
166 // term = B*x**(-(2*k+1))
167 // S += term
168 // return S
169 //
170 // terms = bernoulli_terms(x, 7)
171 // coeffs = [RealField(150)(terms.coefficient(x, n)) for n in range(0, terms.degree(x)+1, 1)]
172 // for k in range(0, 14):
173 // c = terms.coefficient(x, -k) # coefficient of x^(-k)
174 // if c == 0:
175 // continue
176 // print("f64::from_bits(" + double_to_hex(c) + "),")
177 let r = 1. / x;
178 let r2 = r * r;
179 let p = f_polyeval6(
180 r2,
181 f64::from_bits(0x3fc5555555555555),
182 f64::from_bits(0xbfa1111111111111),
183 f64::from_bits(0x3f98618618618618),
184 f64::from_bits(0xbfa1111111111111),
185 f64::from_bits(0x3fb364d9364d9365),
186 f64::from_bits(0xbfd0330330330330),
187 );
188 f_fmla(p, r2 * r, f_fmla(r2, 0.5, r))
189}
190
191/// Computes the trigamma function ψ₁(x).
192///
193/// The trigamma function is the second derivative of the logarithm of the gamma function.
194pub fn f_trigammaf(x: f32) -> f32 {
195 let xb = x.to_bits();
196 // filter out exceptional cases
197 if xb >= 0xffu32 << 23 || xb == 0 {
198 if x.is_infinite() {
199 return if x.is_sign_negative() {
200 f32::NEG_INFINITY
201 } else {
202 0.
203 };
204 }
205 if x.is_nan() {
206 return f32::NAN;
207 }
208 if xb.wrapping_shl(1) == 0 {
209 return f32::INFINITY;
210 }
211 }
212
213 let ax = x.to_bits() & 0x7fff_ffff;
214
215 if ax <= 0x34000000u32 {
216 // |x| < f32::EPSILON
217 let dx = x as f64;
218 return (1. / (dx * dx)) as f32;
219 }
220
221 let mut dx = x as f64;
222
223 let mut result = 0.;
224 let mut sum_parity: f64 = 1.0;
225
226 if x < 0. {
227 // singularity at negative integers
228 if is_integerf(x) {
229 return f32::INFINITY;
230 }
231 // reflection formula
232 // Trigamma[1-x] + Trigamma[x] = PI^2 / sinpi^2(x)
233 const SQR_PI: f64 = f64::from_bits(0x4023bd3cc9be45de); // pi^2
234 let sinpi_ax = fast_sinpif(-x);
235 dx = 1. - dx;
236 result = SQR_PI / (sinpi_ax * sinpi_ax);
237 sum_parity = -1.;
238 }
239
240 let r = approx_trigamma(dx) * sum_parity;
241 result += r;
242 result as f32
243}
244
245#[cfg(test)]
246mod tests {
247 use crate::f_trigammaf;
248
249 #[test]
250 fn test_trigamma() {
251 assert_eq!(f_trigammaf(-27.058018), 300.35904);
252 assert_eq!(f_trigammaf(27.058018), 0.037648965);
253 assert_eq!(f_trigammaf(8.058018), 0.13211797);
254 assert_eq!(f_trigammaf(-8.058018), 300.27863);
255 assert_eq!(f_trigammaf(2.23432), 0.56213206);
256 assert_eq!(f_trigammaf(-2.4653), 9.653673);
257 assert_eq!(f_trigammaf(0.123541), 66.911285);
258 assert_eq!(f_trigammaf(-0.54331), 9.154416);
259 assert_eq!(f_trigammaf(-5.), f32::INFINITY);
260 assert_eq!(f_trigammaf(-0.), f32::INFINITY);
261 assert_eq!(f_trigammaf(0.), f32::INFINITY);
262 assert_eq!(f_trigammaf(f32::INFINITY), 0.0);
263 assert_eq!(f_trigammaf(f32::NEG_INFINITY), f32::NEG_INFINITY);
264 assert!(f_trigammaf(f32::NAN).is_nan());
265 }
266}