/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/gamma/trigammaf.rs
Line | Count | Source |
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 | | */ |
29 | | use crate::common::{f_fmla, is_integerf}; |
30 | | use crate::polyeval::{f_polyeval6, f_polyeval10}; |
31 | | use crate::sin_cosf::fast_sinpif; |
32 | | |
33 | | #[inline] |
34 | 0 | fn approx_trigamma(x: f64) -> f64 { |
35 | 0 | 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 | 0 | let p_num = f_polyeval10( |
50 | 0 | x, |
51 | 0 | f64::from_bits(0x3ffa51a6625307d3), |
52 | 0 | f64::from_bits(0x40142fc9c4f02b3f), |
53 | 0 | f64::from_bits(0x401b30a762805b44), |
54 | 0 | f64::from_bits(0x4014dc84c95656bd), |
55 | 0 | f64::from_bits(0x4003e44f4c820b4c), |
56 | 0 | f64::from_bits(0x3fe81f37523197d3), |
57 | 0 | f64::from_bits(0x3fc22bffe2490221), |
58 | 0 | f64::from_bits(0x3f8f221a6329ea36), |
59 | 0 | f64::from_bits(0x3f47406930b9563c), |
60 | 0 | f64::from_bits(0xbd99cd44c6ad497a), |
61 | | ); |
62 | 0 | let p_den = f_polyeval10( |
63 | 0 | x, |
64 | 0 | f64::from_bits(0x3ff0000000000000), |
65 | 0 | f64::from_bits(0x40121e3db4e0a2f3), |
66 | 0 | f64::from_bits(0x40218e97a5430c4f), |
67 | 0 | f64::from_bits(0x402329897737b159), |
68 | 0 | f64::from_bits(0x401a0fdc27807c2d), |
69 | 0 | f64::from_bits(0x4006ff242e1f3a51), |
70 | 0 | f64::from_bits(0x3fea6eda129c4e85), |
71 | 0 | f64::from_bits(0x3fc32700b2ae2e88), |
72 | 0 | f64::from_bits(0x3f8fdc1dc6116d41), |
73 | 0 | f64::from_bits(0x3f4740690261cfbc), |
74 | | ); |
75 | 0 | return p_num / p_den + 1. / (x * x); |
76 | 0 | } 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 | 0 | let p_num = f_polyeval10( |
91 | 0 | x, |
92 | 0 | f64::from_bits(0x3ffa51a6625307d3), |
93 | 0 | f64::from_bits(0x4015167fa2d2b5a8), |
94 | 0 | f64::from_bits(0x401dd40865d5985e), |
95 | 0 | f64::from_bits(0x4018353d9425fb58), |
96 | 0 | f64::from_bits(0x4008a12aa45851fa), |
97 | 0 | f64::from_bits(0x3ff018736d0c5dbe), |
98 | 0 | f64::from_bits(0x3fca715702bdb519), |
99 | 0 | f64::from_bits(0x3f9908a9d73d983c), |
100 | 0 | f64::from_bits(0x3f54fd9cbbb46314), |
101 | 0 | f64::from_bits(0xbd00b8a28c578ab5), |
102 | | ); |
103 | 0 | let p_den = f_polyeval10( |
104 | 0 | x, |
105 | 0 | f64::from_bits(0x3ff0000000000000), |
106 | 0 | f64::from_bits(0x4012aa7f041a768b), |
107 | 0 | f64::from_bits(0x4022c2604e5f9c7a), |
108 | 0 | f64::from_bits(0x4025655b63c2db22), |
109 | 0 | f64::from_bits(0x401eaa8e59c8295d), |
110 | 0 | f64::from_bits(0x400cc8724a58809c), |
111 | 0 | f64::from_bits(0x3ff1c7a91c8e3c40), |
112 | 0 | f64::from_bits(0x3fcc05613a11183e), |
113 | 0 | f64::from_bits(0x3f99b096bd3ce542), |
114 | 0 | f64::from_bits(0x3f54fd9cbb9c6167), |
115 | | ); |
116 | 0 | return p_num / p_den + 1. / (x * x); |
117 | 0 | } 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 | 0 | let p_num = f_polyeval10( |
132 | 0 | x, |
133 | 0 | f64::from_bits(0x3ffa51a664b1b211), |
134 | 0 | f64::from_bits(0x4016d7f75881312a), |
135 | 0 | f64::from_bits(0x4021b10defb47bcc), |
136 | 0 | f64::from_bits(0x401fe9633665e1bf), |
137 | 0 | f64::from_bits(0x4012601cce6766d7), |
138 | 0 | f64::from_bits(0x3ffbd0ece1c435f1), |
139 | 0 | f64::from_bits(0x3fdb3fd0e233c485), |
140 | 0 | f64::from_bits(0x3faffdedea90b870), |
141 | 0 | f64::from_bits(0x3f71a4bbf0d00147), |
142 | 0 | f64::from_bits(0xbc0aae286498a357), |
143 | | ); |
144 | 0 | let p_den = f_polyeval10( |
145 | 0 | x, |
146 | 0 | f64::from_bits(0x3ff0000000000000), |
147 | 0 | f64::from_bits(0x4013bbbd604d685d), |
148 | 0 | f64::from_bits(0x40253a4fca05438e), |
149 | 0 | f64::from_bits(0x402a4aba4634f14f), |
150 | 0 | f64::from_bits(0x4024cdd23bd6284a), |
151 | 0 | f64::from_bits(0x4015fbe371275c3f), |
152 | 0 | f64::from_bits(0x3fff4d7ebf7d1ed0), |
153 | 0 | f64::from_bits(0x3fdd459154d7bc72), |
154 | 0 | f64::from_bits(0x3fb08c1cd4cedca3), |
155 | 0 | f64::from_bits(0x3f71a4bbf0d0012d), |
156 | | ); |
157 | 0 | return p_num / p_den + 1. / (x * x); |
158 | 0 | } |
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 | 0 | let r = 1. / x; |
178 | 0 | let r2 = r * r; |
179 | 0 | let p = f_polyeval6( |
180 | 0 | r2, |
181 | 0 | f64::from_bits(0x3fc5555555555555), |
182 | 0 | f64::from_bits(0xbfa1111111111111), |
183 | 0 | f64::from_bits(0x3f98618618618618), |
184 | 0 | f64::from_bits(0xbfa1111111111111), |
185 | 0 | f64::from_bits(0x3fb364d9364d9365), |
186 | 0 | f64::from_bits(0xbfd0330330330330), |
187 | | ); |
188 | 0 | f_fmla(p, r2 * r, f_fmla(r2, 0.5, r)) |
189 | 0 | } |
190 | | |
191 | | /// Computes the trigamma function ψ₁(x). |
192 | | /// |
193 | | /// The trigamma function is the second derivative of the logarithm of the gamma function. |
194 | 0 | pub fn f_trigammaf(x: f32) -> f32 { |
195 | 0 | let xb = x.to_bits(); |
196 | | // filter out exceptional cases |
197 | 0 | if xb >= 0xffu32 << 23 || xb == 0 { |
198 | 0 | if x.is_infinite() { |
199 | 0 | return if x.is_sign_negative() { |
200 | 0 | f32::NEG_INFINITY |
201 | | } else { |
202 | 0 | 0. |
203 | | }; |
204 | 0 | } |
205 | 0 | if x.is_nan() { |
206 | 0 | return f32::NAN; |
207 | 0 | } |
208 | 0 | if xb.wrapping_shl(1) == 0 { |
209 | 0 | return f32::INFINITY; |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | 0 | let ax = x.to_bits() & 0x7fff_ffff; |
214 | | |
215 | 0 | if ax <= 0x34000000u32 { |
216 | | // |x| < f32::EPSILON |
217 | 0 | let dx = x as f64; |
218 | 0 | return (1. / (dx * dx)) as f32; |
219 | 0 | } |
220 | | |
221 | 0 | let mut dx = x as f64; |
222 | | |
223 | 0 | let mut result = 0.; |
224 | 0 | let mut sum_parity: f64 = 1.0; |
225 | | |
226 | 0 | if x < 0. { |
227 | | // singularity at negative integers |
228 | 0 | if is_integerf(x) { |
229 | 0 | return f32::INFINITY; |
230 | 0 | } |
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 | 0 | let sinpi_ax = fast_sinpif(-x); |
235 | 0 | dx = 1. - dx; |
236 | 0 | result = SQR_PI / (sinpi_ax * sinpi_ax); |
237 | 0 | sum_parity = -1.; |
238 | 0 | } |
239 | | |
240 | 0 | let r = approx_trigamma(dx) * sum_parity; |
241 | 0 | result += r; |
242 | 0 | result as f32 |
243 | 0 | } |
244 | | |
245 | | #[cfg(test)] |
246 | | mod 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 | | } |