WPILibC++ 2024.3.2
erf_inv.hpp
Go to the documentation of this file.
1/*################################################################################
2 ##
3 ## Copyright (C) 2016-2023 Keith O'Hara
4 ##
5 ## This file is part of the GCE-Math C++ library.
6 ##
7 ## Licensed under the Apache License, Version 2.0 (the "License");
8 ## you may not use this file except in compliance with the License.
9 ## You may obtain a copy of the License at
10 ##
11 ## http://www.apache.org/licenses/LICENSE-2.0
12 ##
13 ## Unless required by applicable law or agreed to in writing, software
14 ## distributed under the License is distributed on an "AS IS" BASIS,
15 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 ## See the License for the specific language governing permissions and
17 ## limitations under the License.
18 ##
19 ################################################################################*/
20
21/*
22 * compile-time inverse error function
23 *
24 * Initial approximation based on:
25 * 'Approximating the erfinv function' by Mike Giles
26 */
27
28#ifndef _gcem_erf_inv_HPP
29#define _gcem_erf_inv_HPP
30
31namespace gcem
32{
33
34namespace internal
35{
36
37template<typename T>
38constexpr T erf_inv_decision(const T value, const T p, const T direc, const int iter_count) noexcept;
39
40//
41// initial value
42
43// two cases: (1) a < 5; and (2) otherwise
44
45template<typename T>
46constexpr
47T
48erf_inv_initial_val_coef_2(const T a, const T p_term, const int order)
49noexcept
50{
51 return( order == 1 ? T(-0.000200214257L) :
52 order == 2 ? T( 0.000100950558L) + a*p_term :
53 order == 3 ? T( 0.00134934322L) + a*p_term :
54 order == 4 ? T(-0.003673428440L) + a*p_term :
55 order == 5 ? T( 0.005739507730L) + a*p_term :
56 order == 6 ? T(-0.00762246130L) + a*p_term :
57 order == 7 ? T( 0.009438870470L) + a*p_term :
58 order == 8 ? T( 1.001674060000L) + a*p_term :
59 order == 9 ? T( 2.83297682000L) + a*p_term :
60 p_term );
61}
62
63template<typename T>
64constexpr
65T
66erf_inv_initial_val_case_2(const T a, const T p_term, const int order)
67noexcept
68{
69 return( order == 9 ? \
70 // if
71 erf_inv_initial_val_coef_2(a,p_term,order) :
72 // else
73 erf_inv_initial_val_case_2(a,erf_inv_initial_val_coef_2(a,p_term,order),order+1) );
74}
75
76template<typename T>
77constexpr
78T
79erf_inv_initial_val_coef_1(const T a, const T p_term, const int order)
80noexcept
81{
82 return( order == 1 ? T( 2.81022636e-08L) :
83 order == 2 ? T( 3.43273939e-07L) + a*p_term :
84 order == 3 ? T(-3.5233877e-06L) + a*p_term :
85 order == 4 ? T(-4.39150654e-06L) + a*p_term :
86 order == 5 ? T( 0.00021858087L) + a*p_term :
87 order == 6 ? T(-0.00125372503L) + a*p_term :
88 order == 7 ? T(-0.004177681640L) + a*p_term :
89 order == 8 ? T( 0.24664072700L) + a*p_term :
90 order == 9 ? T( 1.50140941000L) + a*p_term :
91 p_term );
92}
93
94template<typename T>
95constexpr
96T
97erf_inv_initial_val_case_1(const T a, const T p_term, const int order)
98noexcept
99{
100 return( order == 9 ? \
101 // if
102 erf_inv_initial_val_coef_1(a,p_term,order) :
103 // else
104 erf_inv_initial_val_case_1(a,erf_inv_initial_val_coef_1(a,p_term,order),order+1) );
105}
106
107template<typename T>
108constexpr
109T
111noexcept
112{
113 return( a < T(5) ? \
114 // if
115 erf_inv_initial_val_case_1(a-T(2.5),T(0),1) :
116 // else
117 erf_inv_initial_val_case_2(sqrt(a)-T(3),T(0),1) );
118}
119
120template<typename T>
121constexpr
122T
124noexcept
125{
126 return x*erf_inv_initial_val_int( -log( (T(1) - x)*(T(1) + x) ) );
127}
128
129//
130// Halley recursion
131
132template<typename T>
133constexpr
134T
135erf_inv_err_val(const T value, const T p)
136noexcept
137{ // err_val = f(x)
138 return( erf(value) - p );
139}
140
141template<typename T>
142constexpr
143T
144erf_inv_deriv_1(const T value)
145noexcept
146{ // derivative of the error function w.r.t. x
147 return( exp( -value*value ) );
148}
149
150template<typename T>
151constexpr
152T
153erf_inv_deriv_2(const T value, const T deriv_1)
154noexcept
155{ // second derivative of the error function w.r.t. x
156 return( deriv_1*( -T(2)*value ) );
157}
158
159template<typename T>
160constexpr
161T
162erf_inv_ratio_val_1(const T value, const T p, const T deriv_1)
163noexcept
164{
165 return( erf_inv_err_val(value,p) / deriv_1 );
166}
167
168template<typename T>
169constexpr
170T
171erf_inv_ratio_val_2(const T value, const T deriv_1)
172noexcept
173{
174 return( erf_inv_deriv_2(value,deriv_1) / deriv_1 );
175}
176
177template<typename T>
178constexpr
179T
180erf_inv_halley(const T ratio_val_1, const T ratio_val_2)
181noexcept
182{
183 return( ratio_val_1 / max( T(0.8), min( T(1.2), T(1) - T(0.5)*ratio_val_1*ratio_val_2 ) ) );
184}
185
186template<typename T>
187constexpr
188T
189erf_inv_recur(const T value, const T p, const T deriv_1, const int iter_count)
190noexcept
191{
192 return erf_inv_decision( value, p,
193 erf_inv_halley(erf_inv_ratio_val_1(value,p,deriv_1),
194 erf_inv_ratio_val_2(value,deriv_1)),
195 iter_count );
196}
197
198template<typename T>
199constexpr
200T
201erf_inv_decision(const T value, const T p, const T direc, const int iter_count)
202noexcept
203{
204 return( iter_count < GCEM_ERF_INV_MAX_ITER ? \
205 // if
206 erf_inv_recur(value-direc,p, erf_inv_deriv_1(value), iter_count+1) :
207 // else
208 value - direc );
209}
210
211template<typename T>
212constexpr
213T
214erf_inv_recur_begin(const T initial_val, const T p)
215noexcept
216{
217 return erf_inv_recur(initial_val,p,erf_inv_deriv_1(initial_val),1);
218}
219
220template<typename T>
221constexpr
222T
224noexcept
225{
226 return( // NaN check
227 is_nan(p) ? \
229 // bad values
230 abs(p) > T(1) ? \
232 // indistinguishable from 1
233 GCLIM<T>::min() > abs(T(1) - p) ? \
235 // indistinguishable from - 1
236 GCLIM<T>::min() > abs(T(1) + p) ? \
238 // else
240}
241
242}
243
244/**
245 * Compile-time inverse Gaussian error function
246 *
247 * @param p a real-valued input with values in the unit-interval.
248 * @return Computes the inverse Gaussian error function, a value \f$ x \f$ such that
249 * \f[ f(x) := \text{erf}(x) - p \f]
250 * is equal to zero, for a given \c p.
251 * GCE-Math finds this root using Halley's method:
252 * \f[ x_{n+1} = x_n - \frac{f(x_n)/f'(x_n)}{1 - 0.5 \frac{f(x_n)}{f'(x_n)} \frac{f''(x_n)}{f'(x_n)} } \f]
253 * where
254 * \f[ \frac{\partial}{\partial x} \text{erf}(x) = \exp(-x^2), \ \ \frac{\partial^2}{\partial x^2} \text{erf}(x) = -2x\exp(-x^2) \f]
255 */
256
257template<typename T>
258constexpr
259return_t<T>
260erf_inv(const T p)
261noexcept
262{
263 return internal::erf_inv_begin( static_cast<return_t<T>>(p) );
264}
265
266}
267
268#endif
#define GCEM_ERF_INV_MAX_ITER
Definition: gcem_options.hpp:141
constexpr T erf_inv_initial_val_case_1(const T a, const T p_term, const int order) noexcept
Definition: erf_inv.hpp:97
constexpr T erf_inv_recur(const T value, const T p, const T deriv_1, const int iter_count) noexcept
Definition: erf_inv.hpp:189
constexpr T erf_inv_decision(const T value, const T p, const T direc, const int iter_count) noexcept
Definition: erf_inv.hpp:201
constexpr T erf_inv_initial_val_coef_2(const T a, const T p_term, const int order) noexcept
Definition: erf_inv.hpp:48
constexpr T erf_inv_deriv_2(const T value, const T deriv_1) noexcept
Definition: erf_inv.hpp:153
constexpr bool is_nan(const T x) noexcept
Definition: is_nan.hpp:39
constexpr T erf_inv_err_val(const T value, const T p) noexcept
Definition: erf_inv.hpp:135
constexpr T erf_inv_halley(const T ratio_val_1, const T ratio_val_2) noexcept
Definition: erf_inv.hpp:180
constexpr T erf_inv_recur_begin(const T initial_val, const T p) noexcept
Definition: erf_inv.hpp:214
constexpr T erf_inv_initial_val_case_2(const T a, const T p_term, const int order) noexcept
Definition: erf_inv.hpp:66
constexpr T erf_inv_ratio_val_1(const T value, const T p, const T deriv_1) noexcept
Definition: erf_inv.hpp:162
constexpr T erf_inv_initial_val_coef_1(const T a, const T p_term, const int order) noexcept
Definition: erf_inv.hpp:79
constexpr T erf_inv_deriv_1(const T value) noexcept
Definition: erf_inv.hpp:144
constexpr T erf_inv_initial_val(const T x) noexcept
Definition: erf_inv.hpp:123
constexpr T erf_inv_ratio_val_2(const T value, const T deriv_1) noexcept
Definition: erf_inv.hpp:171
constexpr T erf_inv_initial_val_int(const T a) noexcept
Definition: erf_inv.hpp:110
constexpr T erf_inv_begin(const T p) noexcept
Definition: erf_inv.hpp:223
Definition: is_even.hpp:29
constexpr T abs(const T x) noexcept
Compile-time absolute value function.
Definition: abs.hpp:40
constexpr return_t< T > erf_inv(const T p) noexcept
Compile-time inverse Gaussian error function.
Definition: erf_inv.hpp:260
constexpr common_t< T1, T2 > max(const T1 x, const T2 y) noexcept
Compile-time pairwise maximum function.
Definition: max.hpp:41
constexpr return_t< T > erf(const T x) noexcept
Compile-time Gaussian error function.
Definition: erf.hpp:143
constexpr return_t< T > log(const T x) noexcept
Compile-time natural logarithm function.
Definition: log.hpp:186
constexpr return_t< T > sqrt(const T x) noexcept
Compile-time square-root function.
Definition: sqrt.hpp:109
constexpr common_t< T1, T2 > min(const T1 x, const T2 y) noexcept
Compile-time pairwise minimum function.
Definition: min.hpp:41
std::numeric_limits< T > GCLIM
Definition: gcem_options.hpp:74
typename std::conditional< std::is_integral< T >::value, double, T >::type return_t
Definition: gcem_options.hpp:77