#ifndef DEF_TOMS748 #define DEF_TOMS748 #include namespace TOMS748 { namespace internal { template T sureAbs(const T & x) { return (x < T(0)) ? -x : x; } // checks that none of the values are the same // returns true if at least two values are identical template bool checkTwoValuesIdentical(T fa, T fb, T fc, T fd) { bool same = false; same |= fa == fb; same |= fa == fc; same |= fa == fd; same |= fb == fc; same |= fb == fd; same |= fc == fd; return same; } // finite differences 1st derivative given a, b, fa, fb template T fbracket1(T a, T b, T fa, T fb) { return (fb-fa)/(b-a); } // finite differences 2nd derivative given a, b, d, fa, fb, fd template T fbracket2(T a, T b, T d, T fa, T fb, T fd) { return (fbracket1(b, d, fb, fd) - fbracket1(a, b, fa, fb))/(d-a); } // standard bracketing routine // returns ahat, bhat, d // d is a point outside the new interval template std::tuple bracket(T a, T b, T c, T fa, T fb, T fc) { if(fa*fc < T(0)) return std::make_tuple(a, c, b, fa, fc, fb); else return std::make_tuple(c, b, a, fc, fb, fa); } // standard bracketing routine // checks if f(c) is close enough to 0 and returns ok = true if it is the case // returns ahat, bhat, d // d is a point outside the new interval template std::tuple bracketAndCheckConvergence(T a, T b, T c, T fa, T fb, T fc, T tol) { bool ok = false; if(internal::sureAbs(fc) < tol) ok = true; if(fa*fc < T(0)) return std::make_tuple(a, c, b, fa, fc, fb, ok); else return std::make_tuple(c, b, a, fc, fb, fa, ok); } // finds an approximate solution to the quadratic P(x) = fa + f[a,b]*(x-a) + f[a,b,d]*(x-a)(x-b) // with f[a,b] = fbracket1(a,b) and f[a,b,d] = fbracket2(a,b,d) template T NewtonQuadratic(T a, T b, T d, T fa, T fb, T fd) { T r; T A = fbracket2(a,b,d,fa,fb,fd); T B = fbracket1(a,b,fa,fb); if(A == T(0)) return a - fa/B; if(A*fa > T(0)) r = a; else r = b; for(int i = 0 ; i < k ; i++) r = r - B*r/(B + A*(T(2)*r - a - b)); return r; } // Inverse cubic interpolation evaluated at 0 (modified Aitken-Neville interpolation) template T ipzero(T a, T b, T c, T d, T fa, T fb, T fc, T fd) { T Q11 = (c-d)*fc/(fd-fc); T Q21 = (b-c)*fb/(fc-fb); T Q31 = (a-b)*fa/(fb-fa); T D21 = (b-c)*fc/(fc-fb); T D31 = (a-b)*fb/(fb-fa); T Q22 = (D21-Q11)*fb/(fd-fb); T Q32 = (D31-Q21)*fa/(fc-fa); T D32 = (D31-Q21)*fc/(fc-fa); T Q33 = (D32-Q22)*fa/(fd-fa); return a + Q31 + Q32 + Q33; } /// Sorts the pairs (abs(a), fa) and (abs(b), fb) in ascending order. template void swap_abs_ab_fafb_if_not_ascending(T & a, T & b, T & fa, T & fb) { if(internal::sureAbs(b) < internal::sureAbs(a)) { // swap a and b T temp = a; a = b; b = temp; // swap fa and fb temp = fa; fa = fb; fb = temp; } } /// Sorts the pairs (a, fa) and (b, fb) in ascending order. template void swap_ab_fafb_if_not_ascending(T & a, T & b, T & fa, T & fb) { if(b < a) { // swap a and b T temp = a; a = b; b = temp; // swap fa and fb temp = fa; fa = fb; fb = temp; } } }// namespace internal /// Algorithm 4.1 from TOMS748 of robust root-solving. /// Use this version if f(a) and f(b) have already been computed. /// Returns x, f(x), and a boolean indicating if the function converged or not (true if converged). /// /// Source : https://na.math.kit.edu/alefeld/download/1995_Algorithm_748_Enclosing_Zeros_of_Continuous_Functions.pdf template std::tuple TOMS748_solve1(Func f, T a, T b, T fa, T fb, T tol, unsigned int Nmax = 1000) { using namespace internal; T c, d, e, u, dbar, dhat, fc, fd, fe, fu, fdbar, fdhat; T mu = 0.5; bool ok; c = a - fa/fbracket1(a, b, fa, fb); // 4.1.1 secant method fc = f(c); std::tie(a, b, d, fa, fb, fd, ok) = bracketAndCheckConvergence(a, b, c, fa, fb, fc, tol); // 4.1.2 if(ok) { return std::make_tuple(c, fc, true); } e = d; fe = fd; // --- for(unsigned int n = 2 ; n < Nmax ; n++) // 4.1.3 { if(n == 2 || checkTwoValuesIdentical(fa, fb, fd, fe)) c = NewtonQuadratic(a, b, d, fa, fb, fd); else { c = ipzero(a, b, d, e, fa, fb, fd, fe); if((c-a)*(c-b) >= T(0)) c = NewtonQuadratic(a, b, d, fa, fb, fd); } // --- fc = f(c); std::tie(a, b, dbar, fa, fb, fdbar, ok) = bracketAndCheckConvergence(a, b, c, fa, fb, fc, tol); // 4.1.4 if(ok) { return std::make_tuple(c, fc, true); } // --- if(internal::sureAbs(fa) < internal::sureAbs(fb)) // 4.1.5 { u = a; fu = fa; } else { u = b; fu = fb; } // --- c = u - 2*fu/fbracket1(a, b, fa, fb); // 4.1.6 // --- if(internal::sureAbs(c - u) > 0.5*(b - a)) // 4.1.7 c = 0.5*(b + a); // --- fc = f(c); std::tie(a, b, dhat, fa, fb, fdhat, ok) = bracketAndCheckConvergence(a, b, c, fa, fb, fc, tol); // 4.1.8 if(ok) { return std::make_tuple(c, fc, true); } // --- if(b - a < mu*(b - a)) // 4.1.9 { d = dhat; e = dbar; fd = fdhat; fe = fdbar; } else { e = dhat; fe = fdhat; } c = 0.5*(a+b); fc = f(c); std::tie(a, b, d, fa, fb, fd, ok) = bracketAndCheckConvergence(a, b, c, fa, fb, fc, tol); if(ok) { return std::make_tuple(c, fc, true); } } return std::make_tuple(c, fc, false);// no solution found, return last estimate } /// Algorithm 4.1 from TOMS748 of robust root-solving. /// Use this version if f(a) and f(b) have NOT already been computed. /// Returns x, f(x), and a boolean indicating if the function converged or not (true if converged). template std::tuple TOMS748_solve1(Func f, T a, T b, T tol, unsigned int Nmax = 1000) { return TOMS748_solve1(f, a, b, f(a), f(b), tol, Nmax); } /// Finds a bracket [a b] that encloses a root of f : f(a)*f(b) < 0, starting from the initial guess x. /// A typical value for the factor is 2. /// The initial guess must be on the correct side of the real line : the algorithm will never cross the zero during the search. /// The factor is increased every 10 iterations in order to speed up convergence for when the root is orders of magnitude away from the initial guess. /// If the bracket returned by the function is too wide, try reducing the factor (while always keeping it > 1). /// /// The function returns a tuple of the following values : /// - T a : lower bound of the interval. /// - T b : upper bound of the interval. /// - T fa : function value at lower bound of the interval. /// - T fb : function value at upper bound of the interval. /// - bool ok : indicates whether the search was successful or not. template std::tuple findEnclosingBracket(Func f, T x, T factor, unsigned int Nmax = 100) { T a, b, fa, fb; unsigned int i; a = x; b = x*factor; // try searching in the positive direction fa = f(a); fb = f(b); internal::swap_abs_ab_fafb_if_not_ascending(a, b, fa, fb); if(fa*fb <= T(0)) // if the original bracket is already enclosing the solution { internal::swap_ab_fafb_if_not_ascending(a, b, fa, fb); return std::make_tuple(a, b, fa, fb, true); } if(internal::sureAbs(fa) < internal::sureAbs(fb)) // if fa is closer, search in the other direction { a = x/factor; b = x; fb = fa; // f(b) = f(x) fa = f(a); internal::swap_abs_ab_fafb_if_not_ascending(a, b, fa, fb); } i = 0; while(i < Nmax && fa*fb > T(0)) { if(internal::sureAbs(fa) < internal::sureAbs(fb)) // fa is closer to 0 than fb, extend the interval in the a direction (reducing it) { a = a / factor; fa = f(a); } else // fb is closer to 0 than fa, extend the interval in the b direction (augmenting it) { b = b * factor; fb = f(b); } i = i+1; if(i % 10 == 0) // every 10 iterations, bump up the factor to speed up convergence factor = factor * 5; } internal::swap_ab_fafb_if_not_ascending(a, b, fa, fb); return std::make_tuple(a, b, fa, fb, static_cast(fa*fb <= T(0))); } /// Finds a bracket that encloses the solution and then calls TOMS748_solve1 to solve for the root of the function in the bracket. /// Returns x, f(x), and a boolean indicating if the function converged or not (true if converged). template std::tuple bracket_and_solve(Func f, T x, T tol, T factor = T(2), unsigned int Nmax = 1000) { T a, b, fa, fb; bool ok; std::tie(a, b, fa, fb, ok) = findEnclosingBracket(f, x, factor, Nmax); if(ok) return TOMS748_solve1(f, a, b, fa, fb, tol, Nmax); else { internal::swap_abs_ab_fafb_if_not_ascending(fa, fb, a, b); return std::make_tuple(a, fa, false); } } }// namespace TOMS748 #endif