// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2015 Eugene Brevdo // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef EIGEN_SPECIAL_FUNCTIONS_H #define EIGEN_SPECIAL_FUNCTIONS_H namespace Eigen { namespace internal { // Parts of this code are based on the Cephes Math Library. // // Cephes Math Library Release 2.8: June, 2000 // Copyright 1984, 1987, 1992, 2000 by Stephen L. Moshier // // Permission has been kindly provided by the original author // to incorporate the Cephes software into the Eigen codebase: // // From: Stephen Moshier // To: Eugene Brevdo // Subject: Re: Permission to wrap several cephes functions in Eigen // // Hello Eugene, // // Thank you for writing. // // If your licensing is similar to BSD, the formal way that has been // handled is simply to add a statement to the effect that you are incorporating // the Cephes software by permission of the author. // // Good luck with your project, // Steve namespace cephes { /* polevl (modified for Eigen) * * Evaluate polynomial * * * * SYNOPSIS: * * int N; * Scalar x, y, coef[N+1]; * * y = polevl( x, coef); * * * * DESCRIPTION: * * Evaluates polynomial of degree N: * * 2 N * y = C + C x + C x +...+ C x * 0 1 2 N * * Coefficients are stored in reverse order: * * coef[0] = C , ..., coef[N] = C . * N 0 * * The function p1evl() assumes that coef[N] = 1.0 and is * omitted from the array. Its calling arguments are * otherwise the same as polevl(). * * * The Eigen implementation is templatized. For best speed, store * coef as a const array (constexpr), e.g. * * const double coef[] = {1.0, 2.0, 3.0, ...}; * */ template struct polevl { EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE static Scalar run(const Scalar x, const Scalar coef[]) { EIGEN_STATIC_ASSERT((N > 0), YOU_MADE_A_PROGRAMMING_MISTAKE); return polevl::run(x, coef) * x + coef[N]; } }; template struct polevl { EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE static Scalar run(const Scalar, const Scalar coef[]) { return coef[0]; } }; } // end namespace cephes /**************************************************************************** * Implementation of lgamma * ****************************************************************************/ template struct lgamma_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Scalar run(const Scalar) { EIGEN_STATIC_ASSERT((internal::is_same::value == false), THIS_TYPE_IS_NOT_SUPPORTED); return Scalar(0); } }; template struct lgamma_retval { typedef Scalar type; }; #ifdef EIGEN_HAS_C99_MATH template <> struct lgamma_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE float run(float x) { return ::lgammaf(x); } }; template <> struct lgamma_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE double run(double x) { return ::lgamma(x); } }; #endif /**************************************************************************** * Implementation of digamma (psi) * ****************************************************************************/ #ifdef EIGEN_HAS_C99_MATH /* * * Polynomial evaluation helper for the Psi (digamma) function. * * digamma_impl_maybe_poly::run(s) evaluates the asymptotic Psi expansion for * input Scalar s, assuming s is above 10.0. * * If s is above a certain threshold for the given Scalar type, zero * is returned. Otherwise the polynomial is evaluated with enough * coefficients for results matching Scalar machine precision. * * */ template struct digamma_impl_maybe_poly { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Scalar run(const Scalar) { EIGEN_STATIC_ASSERT((internal::is_same::value == false), THIS_TYPE_IS_NOT_SUPPORTED); return Scalar(0); } }; template <> struct digamma_impl_maybe_poly { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE float run(const float s) { const float A[] = { -4.16666666666666666667E-3, 3.96825396825396825397E-3, -8.33333333333333333333E-3, 8.33333333333333333333E-2 }; float z; if (s < 1.0e8f) { z = 1.0f / (s * s); return z * cephes::polevl::run(z, A); } else return 0.0f; } }; template <> struct digamma_impl_maybe_poly { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE double run(const double s) { const double A[] = { 8.33333333333333333333E-2, -2.10927960927960927961E-2, 7.57575757575757575758E-3, -4.16666666666666666667E-3, 3.96825396825396825397E-3, -8.33333333333333333333E-3, 8.33333333333333333333E-2 }; double z; if (s < 1.0e17) { z = 1.0 / (s * s); return z * cephes::polevl::run(z, A); } else return 0.0; } }; #endif // EIGEN_HAS_C99_MATH template struct digamma_retval { typedef Scalar type; }; #ifdef EIGEN_HAS_C99_MATH template struct digamma_impl { EIGEN_DEVICE_FUNC static Scalar run(Scalar x) { /* * * Psi (digamma) function (modified for Eigen) * * * SYNOPSIS: * * double x, y, psi(); * * y = psi( x ); * * * DESCRIPTION: * * d - * psi(x) = -- ln | (x) * dx * * is the logarithmic derivative of the gamma function. * For integer x, * n-1 * - * psi(n) = -EUL + > 1/k. * - * k=1 * * If x is negative, it is transformed to a positive argument by the * reflection formula psi(1-x) = psi(x) + pi cot(pi x). * For general positive x, the argument is made greater than 10 * using the recurrence psi(x+1) = psi(x) + 1/x. * Then the following asymptotic expansion is applied: * * inf. B * - 2k * psi(x) = log(x) - 1/2x - > ------- * - 2k * k=1 2k x * * where the B2k are Bernoulli numbers. * * ACCURACY (float): * Relative error (except absolute when |psi| < 1): * arithmetic domain # trials peak rms * IEEE 0,30 30000 1.3e-15 1.4e-16 * IEEE -30,0 40000 1.5e-15 2.2e-16 * * ACCURACY (double): * Absolute error, relative when |psi| > 1 : * arithmetic domain # trials peak rms * IEEE -33,0 30000 8.2e-7 1.2e-7 * IEEE 0,33 100000 7.3e-7 7.7e-8 * * ERROR MESSAGES: * message condition value returned * psi singularity x integer <=0 INFINITY */ Scalar p, q, nz, s, w, y; bool negative; const Scalar maxnum = std::numeric_limits::infinity(); const Scalar m_pi = 3.14159265358979323846; negative = 0; nz = 0.0; const Scalar zero = 0.0; const Scalar one = 1.0; const Scalar half = 0.5; if (x <= zero) { negative = one; q = x; p = ::floor(q); if (p == q) { return maxnum; } /* Remove the zeros of tan(m_pi x) * by subtracting the nearest integer from x */ nz = q - p; if (nz != half) { if (nz > half) { p += one; nz = q - p; } nz = m_pi / ::tan(m_pi * nz); } else { nz = zero; } x = one - x; } /* use the recurrence psi(x+1) = psi(x) + 1/x. */ s = x; w = zero; while (s < Scalar(10)) { w += one / s; s += one; } y = digamma_impl_maybe_poly::run(s); y = ::log(s) - (half / s) - y - w; return (negative) ? y - nz : y; } }; #endif // EIGEN_HAS_C99_MATH /**************************************************************************** * Implementation of erf * ****************************************************************************/ template struct erf_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Scalar run(const Scalar) { EIGEN_STATIC_ASSERT((internal::is_same::value == false), THIS_TYPE_IS_NOT_SUPPORTED); return Scalar(0); } }; template struct erf_retval { typedef Scalar type; }; #ifdef EIGEN_HAS_C99_MATH template <> struct erf_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE float run(float x) { return ::erff(x); } }; template <> struct erf_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE double run(double x) { return ::erf(x); } }; #endif // EIGEN_HAS_C99_MATH /*************************************************************************** * Implementation of erfc * ****************************************************************************/ template struct erfc_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Scalar run(const Scalar) { EIGEN_STATIC_ASSERT((internal::is_same::value == false), THIS_TYPE_IS_NOT_SUPPORTED); return Scalar(0); } }; template struct erfc_retval { typedef Scalar type; }; #ifdef EIGEN_HAS_C99_MATH template <> struct erfc_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE float run(const float x) { return ::erfcf(x); } }; template <> struct erfc_impl { EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE double run(const double x) { return ::erfc(x); } }; #endif // EIGEN_HAS_C99_MATH } // end namespace internal namespace numext { template EIGEN_DEVICE_FUNC inline EIGEN_MATHFUNC_RETVAL(lgamma, Scalar) lgamma(const Scalar& x) { return EIGEN_MATHFUNC_IMPL(lgamma, Scalar)::run(x); } template EIGEN_DEVICE_FUNC inline EIGEN_MATHFUNC_RETVAL(digamma, Scalar) digamma(const Scalar& x) { return EIGEN_MATHFUNC_IMPL(digamma, Scalar)::run(x); } template EIGEN_DEVICE_FUNC inline EIGEN_MATHFUNC_RETVAL(erf, Scalar) erf(const Scalar& x) { return EIGEN_MATHFUNC_IMPL(erf, Scalar)::run(x); } template EIGEN_DEVICE_FUNC inline EIGEN_MATHFUNC_RETVAL(erfc, Scalar) erfc(const Scalar& x) { return EIGEN_MATHFUNC_IMPL(erfc, Scalar)::run(x); } } // end namespace numext } // end namespace Eigen #endif // EIGEN_SPECIAL_FUNCTIONS_H