WPILibC++ 2023.4.3-108-ge5452e3
erf_inv.hpp
Go to the documentation of this file.
1/*################################################################################
2 ##
3 ## Copyright (C) 2016-2022 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 internal
32{
33
34template<typename T>
35constexpr T erf_inv_decision(const T value, const T p, const T direc, const int iter_count) noexcept;
36
37//
38// initial value
39
40// two cases: (1) a < 5; and (2) otherwise
41
42template<typename T>
43constexpr
44T
45erf_inv_initial_val_coef_2(const T a, const T p_term, const int order)
46noexcept
47{
48 return( order == 1 ? T(-0.000200214257L) :
49 order == 2 ? T( 0.000100950558L) + a*p_term :
50 order == 3 ? T( 0.00134934322L) + a*p_term :
51 order == 4 ? T(-0.003673428440L) + a*p_term :
52 order == 5 ? T( 0.005739507730L) + a*p_term :
53 order == 6 ? T(-0.00762246130L) + a*p_term :
54 order == 7 ? T( 0.009438870470L) + a*p_term :
55 order == 8 ? T( 1.001674060000L) + a*p_term :
56 order == 9 ? T( 2.83297682000L) + a*p_term :
57 p_term );
58}
59
60template<typename T>
61constexpr
62T
63erf_inv_initial_val_case_2(const T a, const T p_term, const int order)
64noexcept
65{
66 return( order == 9 ? \
67 // if
68 erf_inv_initial_val_coef_2(a,p_term,order) :
69 // else
70 erf_inv_initial_val_case_2(a,erf_inv_initial_val_coef_2(a,p_term,order),order+1) );
71}
72
73template<typename T>
74constexpr
75T
76erf_inv_initial_val_coef_1(const T a, const T p_term, const int order)
77noexcept
78{
79 return( order == 1 ? T( 2.81022636e-08L) :
80 order == 2 ? T( 3.43273939e-07L) + a*p_term :
81 order == 3 ? T(-3.5233877e-06L) + a*p_term :
82 order == 4 ? T(-4.39150654e-06L) + a*p_term :
83 order == 5 ? T( 0.00021858087L) + a*p_term :
84 order == 6 ? T(-0.00125372503L) + a*p_term :
85 order == 7 ? T(-0.004177681640L) + a*p_term :
86 order == 8 ? T( 0.24664072700L) + a*p_term :
87 order == 9 ? T( 1.50140941000L) + a*p_term :
88 p_term );
89}
90
91template<typename T>
92constexpr
93T
94erf_inv_initial_val_case_1(const T a, const T p_term, const int order)
95noexcept
96{
97 return( order == 9 ? \
98 // if
99 erf_inv_initial_val_coef_1(a,p_term,order) :
100 // else
101 erf_inv_initial_val_case_1(a,erf_inv_initial_val_coef_1(a,p_term,order),order+1) );
102}
103
104template<typename T>
105constexpr
106T
108noexcept
109{
110 return( a < T(5) ? \
111 // if
112 erf_inv_initial_val_case_1(a-T(2.5),T(0),1) :
113 // else
114 erf_inv_initial_val_case_2(sqrt(a)-T(3),T(0),1) );
115}
116
117template<typename T>
118constexpr
119T
121noexcept
122{
123 return x*erf_inv_initial_val_int( -log( (T(1) - x)*(T(1) + x) ) );
124}
125
126//
127// Halley recursion
128
129template<typename T>
130constexpr
131T
132erf_inv_err_val(const T value, const T p)
133noexcept
134{ // err_val = f(x)
135 return( erf(value) - p );
136}
137
138template<typename T>
139constexpr
140T
142noexcept
143{ // derivative of the error function w.r.t. x
144 return( exp( -value*value ) );
145}
146
147template<typename T>
148constexpr
149T
150erf_inv_deriv_2(const T value, const T deriv_1)
151noexcept
152{ // second derivative of the error function w.r.t. x
153 return( deriv_1*( -T(2)*value ) );
154}
155
156template<typename T>
157constexpr
158T
159erf_inv_ratio_val_1(const T value, const T p, const T deriv_1)
160noexcept
161{
162 return( erf_inv_err_val(value,p) / deriv_1 );
163}
164
165template<typename T>
166constexpr
167T
168erf_inv_ratio_val_2(const T value, const T deriv_1)
169noexcept
170{
171 return( erf_inv_deriv_2(value,deriv_1) / deriv_1 );
172}
173
174template<typename T>
175constexpr
176T
177erf_inv_halley(const T ratio_val_1, const T ratio_val_2)
178noexcept
179{
180 return( ratio_val_1 / max( T(0.8), min( T(1.2), T(1) - T(0.5)*ratio_val_1*ratio_val_2 ) ) );
181}
182
183template<typename T>
184constexpr
185T
186erf_inv_recur(const T value, const T p, const T deriv_1, const int iter_count)
187noexcept
188{
189 return erf_inv_decision( value, p,
191 erf_inv_ratio_val_2(value,deriv_1)),
192 iter_count );
193}
194
195template<typename T>
196constexpr
197T
198erf_inv_decision(const T value, const T p, const T direc, const int iter_count)
199noexcept
200{
201 return( iter_count < GCEM_ERF_INV_MAX_ITER ? \
202 // if
203 erf_inv_recur(value-direc,p, erf_inv_deriv_1(value), iter_count+1) :
204 // else
205 value - direc );
206}
207
208template<typename T>
209constexpr
210T
211erf_inv_recur_begin(const T initial_val, const T p)
212noexcept
213{
214 return erf_inv_recur(initial_val,p,erf_inv_deriv_1(initial_val),1);
215}
216
217template<typename T>
218constexpr
219T
221noexcept
222{
223 return( // NaN check
224 is_nan(p) ? \
225 GCLIM<T>::quiet_NaN() :
226 // bad values
227 abs(p) > T(1) ? \
228 GCLIM<T>::quiet_NaN() :
229 // indistinguishable from 1
230 GCLIM<T>::min() > abs(T(1) - p) ? \
231 GCLIM<T>::infinity() :
232 // indistinguishable from - 1
233 GCLIM<T>::min() > abs(T(1) + p) ? \
234 - GCLIM<T>::infinity() :
235 // else
237}
238
239}
240
241/**
242 * Compile-time inverse Gaussian error function
243 *
244 * @param p a real-valued input with values in the unit-interval.
245 * @return Computes the inverse Gaussian error function, a value \f$ x \f$ such that
246 * \f[ f(x) := \text{erf}(x) - p \f]
247 * is equal to zero, for a given \c p.
248 * GCE-Math finds this root using Halley's method:
249 * \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]
250 * where
251 * \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]
252 */
253
254template<typename T>
255constexpr
256return_t<T>
257erf_inv(const T p)
258noexcept
259{
260 return internal::erf_inv_begin( static_cast<return_t<T>>(p) );
261}
262
263
264#endif
EIGEN_DEVICE_FUNC const ErfReturnType erf() const
\cpp11
Definition: ArrayCwiseUnaryOps.h:655
Definition: core.h:1240
constexpr return_t< T > erf_inv(const T p) noexcept
Compile-time inverse Gaussian error function.
Definition: erf_inv.hpp:257
#define GCEM_ERF_INV_MAX_ITER
Definition: gcem_options.hpp:141
UnitType abs(const UnitType x) noexcept
Compute absolute value.
Definition: math.h:721
auto sqrt(const UnitType &value) noexcept -> unit_t< square_root< typename units::traits::unit_t_traits< UnitType >::unit_type >, typename units::traits::unit_t_traits< UnitType >::underlying_type, linear_scale >
computes the square root of value
Definition: math.h:483
dimensionless::scalar_t log(const ScalarUnit x) noexcept
Compute natural logarithm.
Definition: math.h:349
constexpr common_t< T1, T2 > max(const T1 x, const T2 y) noexcept
Compile-time pairwise maximum function.
Definition: max.hpp:35
constexpr common_t< T1, T2 > min(const T1 x, const T2 y) noexcept
Compile-time pairwise minimum function.
Definition: min.hpp:35
Definition: Eigen_Colamd.h:50
constexpr T erf_inv_err_val(const T value, const T p) noexcept
Definition: erf_inv.hpp:132
constexpr T erf_inv_initial_val_case_2(const T a, const T p_term, const int order) noexcept
Definition: erf_inv.hpp:63
constexpr T erf_inv_ratio_val_1(const T value, const T p, const T deriv_1) noexcept
Definition: erf_inv.hpp:159
constexpr T erf_inv_ratio_val_2(const T value, const T deriv_1) noexcept
Definition: erf_inv.hpp:168
constexpr bool is_nan(const T x) noexcept
Definition: is_nan.hpp:36
constexpr T erf_inv_initial_val_case_1(const T a, const T p_term, const int order) noexcept
Definition: erf_inv.hpp:94
constexpr T erf_inv_decision(const T value, const T p, const T direc, const int iter_count) noexcept
Definition: erf_inv.hpp:198
constexpr T erf_inv_recur(const T value, const T p, const T deriv_1, const int iter_count) noexcept
Definition: erf_inv.hpp:186
constexpr T erf_inv_recur_begin(const T initial_val, const T p) noexcept
Definition: erf_inv.hpp:211
constexpr T erf_inv_initial_val_coef_2(const T a, const T p_term, const int order) noexcept
Definition: erf_inv.hpp:45
constexpr T erf_inv_begin(const T p) noexcept
Definition: erf_inv.hpp:220
constexpr T erf_inv_deriv_1(const T value) noexcept
Definition: erf_inv.hpp:141
constexpr T erf_inv_initial_val_int(const T a) noexcept
Definition: erf_inv.hpp:107
constexpr T erf_inv_initial_val(const T x) noexcept
Definition: erf_inv.hpp:120
constexpr T erf_inv_halley(const T ratio_val_1, const T ratio_val_2) noexcept
Definition: erf_inv.hpp:177
constexpr T erf_inv_initial_val_coef_1(const T a, const T p_term, const int order) noexcept
Definition: erf_inv.hpp:76
constexpr T erf_inv_deriv_2(const T value, const T deriv_1) noexcept
Definition: erf_inv.hpp:150