Approximation for the inverse error function from Giles, M., "Approximating the erfinv function". The approximation has the form: w = -log((1 - x) * (1 + x)) if ( w < 5 ) { w = w - 2.5 p = sum_{i=1}^n lq[i]*w^i } else { w = sqrt(w) - 3 p = sum_{i=1}^n gq[i]*w^i } return p*x
| 332 | // } |
| 333 | // return p*x |
| 334 | XlaOp ErfInv32(XlaOp x) { |
| 335 | constexpr int kDegree = 9; |
| 336 | constexpr std::array<float, 9> w_less_than_5_constants = { |
| 337 | 2.81022636e-08f, 3.43273939e-07f, -3.5233877e-06f, |
| 338 | -4.39150654e-06f, 0.00021858087f, -0.00125372503f, |
| 339 | -0.00417768164f, 0.246640727f, 1.50140941f}; |
| 340 | constexpr std::array<float, 9> w_greater_than_5_constants = { |
| 341 | -0.000200214257f, 0.000100950558f, 0.00134934322f, |
| 342 | -0.00367342844f, 0.00573950773f, -0.0076224613f, |
| 343 | 0.00943887047f, 1.00167406f, 2.83297682f}; |
| 344 | |
| 345 | // Compute logarithm of (1+arg) using log1p(arg) which is more precise than |
| 346 | // log(1+arg) when arg is close to zero. For more details, see |
| 347 | // https://en.cppreference.com/w/cpp/numeric/math/log1p |
| 348 | auto w = -Log1p(-x * x); |
| 349 | |
| 350 | auto lt = Lt(w, ScalarLike(x, 5.0)); |
| 351 | auto coefficient = [&](int i) { |
| 352 | return Select(lt, FullLike(x, w_less_than_5_constants[i]), |
| 353 | FullLike(x, w_greater_than_5_constants[i])); |
| 354 | }; |
| 355 | w = Select(lt, w - ScalarLike(x, 2.5), Sqrt(w) - ScalarLike(x, 3.0)); |
| 356 | auto p = coefficient(0); |
| 357 | for (int i = 1; i < kDegree; ++i) { |
| 358 | p = coefficient(i) + p * w; |
| 359 | } |
| 360 | |
| 361 | // Result modulo edge cases. |
| 362 | XlaOp result = p * x; |
| 363 | |
| 364 | // Handle edge cases, namely erfinv(+/-1) = +/-inf. (The above computation is |
| 365 | // indeterminate, and can give nan or -/+inf.) |
| 366 | auto& b = *x.builder(); |
| 367 | return b.ReportErrorOrReturn([&]() -> StatusOr<XlaOp> { |
| 368 | TF_ASSIGN_OR_RETURN(Shape shape, b.GetShape(x)); |
| 369 | return Select(Eq(Abs(x), ScalarLike(x, 1)), |
| 370 | x * MaxValue(&b, shape.element_type()), result); |
| 371 | }); |
| 372 | } |
| 373 | |
| 374 | XlaOp ErfInv64(XlaOp x) { |
| 375 | constexpr std::array<double, 23> w_less_than_6_25_constants = { |
no test coverage detected