// (C) Copyright John Maddock 2006. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP #define BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP #ifdef _MSC_VER #pragma once #endif #include // test for multiprecision types. #include // test for complex types #include #include #include #include #include #include #include #include #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4512) #endif #include #ifdef BOOST_MSVC #pragma warning(pop) #endif #include #include #include namespace boost{ namespace math{ namespace tools{ namespace detail{ namespace dummy{ template typename T::value_type get(const T&) BOOST_MATH_NOEXCEPT(T); } template void unpack_tuple(const Tuple& t, T& a, T& b) BOOST_MATH_NOEXCEPT(T) { using dummy::get; // Use ADL to find the right overload for get: a = get<0>(t); b = get<1>(t); } template void unpack_tuple(const Tuple& t, T& a, T& b, T& c) BOOST_MATH_NOEXCEPT(T) { using dummy::get; // Use ADL to find the right overload for get: a = get<0>(t); b = get<1>(t); c = get<2>(t); } template inline void unpack_0(const Tuple& t, T& val) BOOST_MATH_NOEXCEPT(T) { using dummy::get; // Rely on ADL to find the correct overload of get: val = get<0>(t); } template inline void unpack_tuple(const std::pair& p, V& a, V& b) BOOST_MATH_NOEXCEPT(T) { a = p.first; b = p.second; } template inline void unpack_0(const std::pair& p, V& a) BOOST_MATH_NOEXCEPT(T) { a = p.first; } template void handle_zero_derivative(F f, T& last_f0, const T& f0, T& delta, T& result, T& guess, const T& min, const T& max) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { if(last_f0 == 0) { // this must be the first iteration, pretend that we had a // previous one at either min or max: if(result == min) { guess = max; } else { guess = min; } unpack_0(f(guess), last_f0); delta = guess - result; } if(sign(last_f0) * sign(f0) < 0) { // we've crossed over so move in opposite direction to last step: if(delta < 0) { delta = (result - min) / 2; } else { delta = (result - max) / 2; } } else { // move in same direction as last step: if(delta < 0) { delta = (result - max) / 2; } else { delta = (result - min) / 2; } } } } // namespace template std::pair bisect(F f, T min, T max, Tol tol, boost::uintmax_t& max_iter, const Policy& pol) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy::value && BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { T fmin = f(min); T fmax = f(max); if(fmin == 0) { max_iter = 2; return std::make_pair(min, min); } if(fmax == 0) { max_iter = 2; return std::make_pair(max, max); } // // Error checking: // static const char* function = "boost::math::tools::bisect<%1%>"; if(min >= max) { return boost::math::detail::pair_from_single(policies::raise_evaluation_error(function, "Arguments in wrong order in boost::math::tools::bisect (first arg=%1%)", min, pol)); } if(fmin * fmax >= 0) { return boost::math::detail::pair_from_single(policies::raise_evaluation_error(function, "No change of sign in boost::math::tools::bisect, either there is no root to find, or there are multiple roots in the interval (f(min) = %1%).", fmin, pol)); } // // Three function invocations so far: // boost::uintmax_t count = max_iter; if(count < 3) count = 0; else count -= 3; while(count && (0 == tol(min, max))) { T mid = (min + max) / 2; T fmid = f(mid); if((mid == max) || (mid == min)) break; if(fmid == 0) { min = max = mid; break; } else if(sign(fmid) * sign(fmin) < 0) { max = mid; fmax = fmid; } else { min = mid; fmin = fmid; } --count; } max_iter -= count; #ifdef BOOST_MATH_INSTRUMENT std::cout << "Bisection iteration, final count = " << max_iter << std::endl; static boost::uintmax_t max_count = 0; if(max_iter > max_count) { max_count = max_iter; std::cout << "Maximum iterations: " << max_iter << std::endl; } #endif return std::make_pair(min, max); } template inline std::pair bisect(F f, T min, T max, Tol tol, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy >::value && BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { return bisect(f, min, max, tol, max_iter, policies::policy<>()); } template inline std::pair bisect(F f, T min, T max, Tol tol) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy >::value && BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { boost::uintmax_t m = (std::numeric_limits::max)(); return bisect(f, min, max, tol, m, policies::policy<>()); } template T newton_raphson_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { BOOST_MATH_STD_USING T f0(0), f1, last_f0(0); T result = guess; T factor = static_cast(ldexp(1.0, 1 - digits)); T delta = tools::max_value(); T delta1 = tools::max_value(); T delta2 = tools::max_value(); boost::uintmax_t count(max_iter); do{ last_f0 = f0; delta2 = delta1; delta1 = delta; detail::unpack_tuple(f(result), f0, f1); --count; if(0 == f0) break; if(f1 == 0) { // Oops zero derivative!!! #ifdef BOOST_MATH_INSTRUMENT std::cout << "Newton iteration, zero derivative found" << std::endl; #endif detail::handle_zero_derivative(f, last_f0, f0, delta, result, guess, min, max); } else { delta = f0 / f1; } #ifdef BOOST_MATH_INSTRUMENT std::cout << "Newton iteration, delta = " << delta << std::endl; #endif if(fabs(delta * 2) > fabs(delta2)) { // last two steps haven't converged. T shift = (delta > 0) ? (result - min) / 2 : (result - max) / 2; if ((result != 0) && (fabs(shift) > fabs(result))) { delta = sign(delta) * result; // protect against huge jumps! } else delta = shift; // reset delta1/2 so we don't take this branch next time round: delta1 = 3 * delta; delta2 = 3 * delta; } guess = result; result -= delta; if(result <= min) { delta = 0.5F * (guess - min); result = guess - delta; if((result == min) || (result == max)) break; } else if(result >= max) { delta = 0.5F * (guess - max); result = guess - delta; if((result == min) || (result == max)) break; } // update brackets: if(delta > 0) max = guess; else min = guess; }while(count && (fabs(result * factor) < fabs(delta))); max_iter -= count; #ifdef BOOST_MATH_INSTRUMENT std::cout << "Newton Raphson iteration, final count = " << max_iter << std::endl; static boost::uintmax_t max_count = 0; if(max_iter > max_count) { max_count = max_iter; std::cout << "Maximum iterations: " << max_iter << std::endl; } #endif return result; } template inline T newton_raphson_iterate(F f, T guess, T min, T max, int digits) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { boost::uintmax_t m = (std::numeric_limits::max)(); return newton_raphson_iterate(f, guess, min, max, digits, m); } namespace detail{ struct halley_step { template static T step(const T& /*x*/, const T& f0, const T& f1, const T& f2) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T)) { using std::fabs; T denom = 2 * f0; T num = 2 * f1 - f0 * (f2 / f1); T delta; BOOST_MATH_INSTRUMENT_VARIABLE(denom); BOOST_MATH_INSTRUMENT_VARIABLE(num); if((fabs(num) < 1) && (fabs(denom) >= fabs(num) * tools::max_value())) { // possible overflow, use Newton step: delta = f0 / f1; } else delta = denom / num; return delta; } }; template T second_order_root_finder(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { BOOST_MATH_STD_USING T f0(0), f1, f2; T result = guess; T factor = ldexp(static_cast(1.0), 1 - digits); T delta = (std::max)(T(10000000 * guess), T(10000000)); // arbitarily large delta T last_f0 = 0; T delta1 = delta; T delta2 = delta; bool out_of_bounds_sentry = false; #ifdef BOOST_MATH_INSTRUMENT std::cout << "Second order root iteration, limit = " << factor << std::endl; #endif boost::uintmax_t count(max_iter); do{ last_f0 = f0; delta2 = delta1; delta1 = delta; detail::unpack_tuple(f(result), f0, f1, f2); --count; BOOST_MATH_INSTRUMENT_VARIABLE(f0); BOOST_MATH_INSTRUMENT_VARIABLE(f1); BOOST_MATH_INSTRUMENT_VARIABLE(f2); if(0 == f0) break; if(f1 == 0) { // Oops zero derivative!!! #ifdef BOOST_MATH_INSTRUMENT std::cout << "Second order root iteration, zero derivative found" << std::endl; #endif detail::handle_zero_derivative(f, last_f0, f0, delta, result, guess, min, max); } else { if(f2 != 0) { delta = Stepper::step(result, f0, f1, f2); if(delta * f1 / f0 < 0) { // Oh dear, we have a problem as Newton and Halley steps // disagree about which way we should move. Probably // there is cancelation error in the calculation of the // Halley step, or else the derivatives are so small // that their values are basically trash. We will move // in the direction indicated by a Newton step, but // by no more than twice the current guess value, otherwise // we can jump way out of bounds if we're not careful. // See https://svn.boost.org/trac/boost/ticket/8314. delta = f0 / f1; if(fabs(delta) > 2 * fabs(guess)) delta = (delta < 0 ? -1 : 1) * 2 * fabs(guess); } } else delta = f0 / f1; } #ifdef BOOST_MATH_INSTRUMENT std::cout << "Second order root iteration, delta = " << delta << std::endl; #endif T convergence = fabs(delta / delta2); if((convergence > 0.8) && (convergence < 2)) { // last two steps haven't converged. delta = (delta > 0) ? (result - min) / 2 : (result - max) / 2; if ((result != 0) && (fabs(delta) > result)) delta = sign(delta) * result; // protect against huge jumps! // reset delta2 so that this branch will *not* be taken on the // next iteration: delta2 = delta * 3; delta1 = delta * 3; BOOST_MATH_INSTRUMENT_VARIABLE(delta); } guess = result; result -= delta; BOOST_MATH_INSTRUMENT_VARIABLE(result); // check for out of bounds step: if(result < min) { T diff = ((fabs(min) < 1) && (fabs(result) > 1) && (tools::max_value() / fabs(result) < fabs(min))) ? T(1000) : (fabs(min) < 1) && (fabs(tools::max_value() * min) < fabs(result)) ? ((min < 0) != (result < 0)) ? -tools::max_value() : tools::max_value() : T(result / min); if(fabs(diff) < 1) diff = 1 / diff; if(!out_of_bounds_sentry && (diff > 0) && (diff < 3)) { // Only a small out of bounds step, lets assume that the result // is probably approximately at min: delta = 0.99f * (guess - min); result = guess - delta; out_of_bounds_sentry = true; // only take this branch once! } else { delta = (guess - min) / 2; result = guess - delta; if((result == min) || (result == max)) break; } } else if(result > max) { T diff = ((fabs(max) < 1) && (fabs(result) > 1) && (tools::max_value() / fabs(result) < fabs(max))) ? T(1000) : T(result / max); if(fabs(diff) < 1) diff = 1 / diff; if(!out_of_bounds_sentry && (diff > 0) && (diff < 3)) { // Only a small out of bounds step, lets assume that the result // is probably approximately at min: delta = 0.99f * (guess - max); result = guess - delta; out_of_bounds_sentry = true; // only take this branch once! } else { delta = (guess - max) / 2; result = guess - delta; if((result == min) || (result == max)) break; } } // update brackets: if(delta > 0) max = guess; else min = guess; } while(count && (fabs(result * factor) < fabs(delta))); max_iter -= count; #ifdef BOOST_MATH_INSTRUMENT std::cout << "Second order root iteration, final count = " << max_iter << std::endl; #endif return result; } } template T halley_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { return detail::second_order_root_finder(f, guess, min, max, digits, max_iter); } template inline T halley_iterate(F f, T guess, T min, T max, int digits) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { boost::uintmax_t m = (std::numeric_limits::max)(); return halley_iterate(f, guess, min, max, digits, m); } namespace detail{ struct schroder_stepper { template static T step(const T& x, const T& f0, const T& f1, const T& f2) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T)) { using std::fabs; T ratio = f0 / f1; T delta; if((x != 0) && (fabs(ratio / x) < 0.1)) { delta = ratio + (f2 / (2 * f1)) * ratio * ratio; // check second derivative doesn't over compensate: if(delta * ratio < 0) delta = ratio; } else delta = ratio; // fall back to Newton iteration. return delta; } }; } template T schroder_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { return detail::second_order_root_finder(f, guess, min, max, digits, max_iter); } template inline T schroder_iterate(F f, T guess, T min, T max, int digits) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { boost::uintmax_t m = (std::numeric_limits::max)(); return schroder_iterate(f, guess, min, max, digits, m); } // // These two are the old spelling of this function, retained for backwards compatibity just in case: // template T schroeder_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { return detail::second_order_root_finder(f, guess, min, max, digits, max_iter); } template inline T schroeder_iterate(F f, T guess, T min, T max, int digits) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval()(std::declval()))) { boost::uintmax_t m = (std::numeric_limits::max)(); return schroder_iterate(f, guess, min, max, digits, m); } #ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS /* * Why do we set the default maximum number of iterations to the number of digits in the type? * Because for double roots, the number of digits increases linearly with the number of iterations, * so this default should recover full precision even in this somewhat pathological case. * For isolated roots, the problem is so rapidly convergent that this doesn't matter at all. */ template Complex complex_newton(F g, Complex guess, int max_iterations=std::numeric_limits::digits) { typedef typename Complex::value_type Real; using std::norm; using std::abs; using std::max; // z0, z1, and z2 cannot be the same, in case we immediately need to resort to Muller's Method: Complex z0 = guess + Complex(1,0); Complex z1 = guess + Complex(0,1); Complex z2 = guess; do { auto pair = g(z2); if (norm(pair.second) == 0) { // Muller's method. Notation follows Numerical Recipes, 9.5.2: Complex q = (z2 - z1)/(z1 - z0); auto P0 = g(z0); auto P1 = g(z1); Complex qp1 = static_cast(1)+q; Complex A = q*(pair.first - qp1*P1.first + q*P0.first); Complex B = (static_cast(2)*q+static_cast(1))*pair.first - qp1*qp1*P1.first +q*q*P0.first; Complex C = qp1*pair.first; Complex rad = sqrt(B*B - static_cast(4)*A*C); Complex denom1 = B + rad; Complex denom2 = B - rad; Complex correction = (z1-z2)*static_cast(2)*C; if (norm(denom1) > norm(denom2)) { correction /= denom1; } else { correction /= denom2; } z0 = z1; z1 = z2; z2 = z2 + correction; } else { z0 = z1; z1 = z2; z2 = z2 - (pair.first/pair.second); } // See: https://math.stackexchange.com/questions/3017766/constructing-newton-iteration-converging-to-non-root // If f' is continuous, then convergence of x_n -> x* implies f(x*) = 0. // This condition approximates this convergence condition by requiring three consecutive iterates to be clustered. Real tol = max(abs(z2)*std::numeric_limits::epsilon(), std::numeric_limits::epsilon()); bool real_close = abs(z0.real() - z1.real()) < tol && abs(z0.real() - z2.real()) < tol && abs(z1.real() - z2.real()) < tol; bool imag_close = abs(z0.imag() - z1.imag()) < tol && abs(z0.imag() - z2.imag()) < tol && abs(z1.imag() - z2.imag()) < tol; if (real_close && imag_close) { return z2; } } while(max_iterations--); // The idea is that if we can get abs(f) < eps, we should, but if we go through all these iterations // and abs(f) < sqrt(eps), then roundoff error simply does not allow that we can evaluate f to < eps // This is somewhat awkward as it isn't scale invariant, but using the Daubechies coefficient example code, // I found this condition generates correct roots, whereas the scale invariant condition discussed here: // https://scicomp.stackexchange.com/questions/30597/defining-a-condition-number-and-termination-criteria-for-newtons-method // allows nonroots to be passed off as roots. auto pair = g(z2); if (abs(pair.first) < sqrt(std::numeric_limits::epsilon())) { return z2; } return {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}; } #endif #if !defined(BOOST_NO_CXX17_IF_CONSTEXPR) // https://stackoverflow.com/questions/48979861/numerically-stable-method-for-solving-quadratic-equations/50065711 namespace detail { template inline T discriminant(T const & a, T const & b, T const & c) { T w = 4*a*c; T e = std::fma(-c, 4*a, w); T f = std::fma(b, b, -w); return f + e; } } template auto quadratic_roots(T const& a, T const& b, T const& c) { using std::copysign; using std::sqrt; if constexpr (std::is_integral::value) { // What I want is to write: // return quadratic_roots(double(a), double(b), double(c)); // but that doesn't compile. double nan = std::numeric_limits::quiet_NaN(); if(a==0) { if (b==0 && c != 0) { return std::pair(nan, nan); } else if (b==0 && c==0) { return std::pair(0,0); } return std::pair(-c/b, -c/b); } if (b==0) { double x0_sq = -double(c)/double(a); if (x0_sq < 0) { return std::pair(nan, nan); } double x0 = sqrt(x0_sq); return std::pair(-x0,x0); } double discriminant = detail::discriminant(double(a), double(b), double(c)); if (discriminant < 0) { return std::pair(nan, nan); } double q = -(b + copysign(sqrt(discriminant), double(b)))/T(2); double x0 = q/a; double x1 = c/q; if (x0 < x1) { return std::pair(x0, x1); } return std::pair(x1, x0); } else if constexpr (std::is_floating_point::value) { T nan = std::numeric_limits::quiet_NaN(); if(a==0) { if (b==0 && c != 0) { return std::pair(nan, nan); } else if (b==0 && c==0) { return std::pair(0,0); } return std::pair(-c/b, -c/b); } if (b==0) { T x0_sq = -c/a; if (x0_sq < 0) { return std::pair(nan, nan); } T x0 = sqrt(x0_sq); return std::pair(-x0,x0); } T discriminant = detail::discriminant(a, b, c); // Is there a sane way to flush very small negative values to zero? // If there is I don't know of it. if (discriminant < 0) { return std::pair(nan, nan); } T q = -(b + copysign(sqrt(discriminant), b))/T(2); T x0 = q/a; T x1 = c/q; if (x0 < x1) { return std::pair(x0, x1); } return std::pair(x1, x0); } else if constexpr (boost::is_complex::value || boost::multiprecision::number_category::value == boost::multiprecision::number_kind_complex) { typename T::value_type nan = std::numeric_limits::quiet_NaN(); if(a.real()==0 && a.imag() ==0) { using std::norm; if (b.real()==0 && b.imag() && norm(c) != 0) { return std::pair({nan, nan}, {nan, nan}); } else if (b.real()==0 && b.imag() && c.real() ==0 && c.imag() == 0) { return std::pair({0,0},{0,0}); } return std::pair(-c/b, -c/b); } if (b.real()==0 && b.imag() == 0) { T x0_sq = -c/a; T x0 = sqrt(x0_sq); return std::pair(-x0, x0); } // There's no fma for complex types: T discriminant = b*b - T(4)*a*c; T q = -(b + sqrt(discriminant))/T(2); return std::pair(q/a, c/q); } else // Most likely the type is a boost.multiprecision. { //There is no fma for multiprecision, and in addition it doesn't seem to be useful, so revert to the naive computation. T nan = std::numeric_limits::quiet_NaN(); if(a==0) { if (b==0 && c != 0) { return std::pair(nan, nan); } else if (b==0 && c==0) { return std::pair(0,0); } return std::pair(-c/b, -c/b); } if (b==0) { T x0_sq = -c/a; if (x0_sq < 0) { return std::pair(nan, nan); } T x0 = sqrt(x0_sq); return std::pair(-x0,x0); } T discriminant = b*b - 4*a*c; if (discriminant < 0) { return std::pair(nan, nan); } T q = -(b + copysign(sqrt(discriminant), b))/T(2); T x0 = q/a; T x1 = c/q; if (x0 < x1) { return std::pair(x0, x1); } return std::pair(x1, x0); } } #endif } // namespace tools } // namespace math } // namespace boost #endif // BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP