|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r56598 - in trunk/boost/math: policies special_functions special_functions/detail tools
From: john_at_[hidden]
Date: 2009-10-05 13:36:59
Author: johnmaddock
Date: 2009-10-05 13:36:56 EDT (Mon, 05 Oct 2009)
New Revision: 56598
URL: http://svn.boost.org/trac/boost/changeset/56598
Log:
Add a check on iterations when Halley iterating.
Correct igamma inverse initial calculations, and adjust digits needed for convergence.
Fix erf calculation limits.
Adjust non-convergence behaviour in Halley iteration.
For issue #3408.
Text files modified:
trunk/boost/math/policies/error_handling.hpp | 9 ++++
trunk/boost/math/special_functions/cbrt.hpp | 7 ++
trunk/boost/math/special_functions/detail/erf_inv.hpp | 6 +
trunk/boost/math/special_functions/detail/ibeta_inverse.hpp | 4 +
trunk/boost/math/special_functions/detail/igamma_inverse.hpp | 84 +++++++++++++++++++++++++++++++--------
trunk/boost/math/special_functions/erf.hpp | 24 ++++++----
trunk/boost/math/tools/roots.hpp | 2
7 files changed, 103 insertions(+), 33 deletions(-)
Modified: trunk/boost/math/policies/error_handling.hpp
==============================================================================
--- trunk/boost/math/policies/error_handling.hpp (original)
+++ trunk/boost/math/policies/error_handling.hpp 2009-10-05 13:36:56 EDT (Mon, 05 Oct 2009)
@@ -632,6 +632,15 @@
"Series evaluation exceeded %1% iterations, giving up now.", max_iter, pol);
}
+template <class Policy>
+inline void check_root_iterations(const char* function, boost::uintmax_t max_iter, const Policy& pol)
+{
+ if(max_iter >= policies::get_max_root_iterations<Policy>())
+ raise_evaluation_error<boost::uintmax_t>(
+ function,
+ "Root finding evaluation exceeded %1% iterations, giving up now.", max_iter, pol);
+}
+
} //namespace policies
#ifdef BOOST_MSVC
Modified: trunk/boost/math/special_functions/cbrt.hpp
==============================================================================
--- trunk/boost/math/special_functions/cbrt.hpp (original)
+++ trunk/boost/math/special_functions/cbrt.hpp 2009-10-05 13:36:56 EDT (Mon, 05 Oct 2009)
@@ -32,7 +32,7 @@
};
template <class T, class Policy>
-T cbrt_imp(T z, const Policy&)
+T cbrt_imp(T z, const Policy& pol)
{
BOOST_MATH_STD_USING
int i_exp, sign(1);
@@ -49,7 +49,10 @@
T max = static_cast<T>(ldexp(2.0, i_exp/3));
T guess = static_cast<T>(ldexp(1.0, i_exp/3));
int digits = (policies::digits<T, Policy>()) / 2;
- return sign * tools::halley_iterate(detail::cbrt_functor<T>(z), guess, min, max, digits);
+ boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
+ guess = sign * tools::halley_iterate(detail::cbrt_functor<T>(z), guess, min, max, digits, max_iter);
+ policies::check_root_iterations("boost::math::cbrt<%1%>", max_iter, pol);
+ return guess;
}
} // namespace detail
Modified: trunk/boost/math/special_functions/detail/erf_inv.hpp
==============================================================================
--- trunk/boost/math/special_functions/detail/erf_inv.hpp (original)
+++ trunk/boost/math/special_functions/detail/erf_inv.hpp 2009-10-05 13:36:56 EDT (Mon, 05 Oct 2009)
@@ -304,14 +304,16 @@
//
if(policies::digits<T, Policy>() > 64)
{
+ boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
if(p <= 0.5)
{
- result = tools::halley_iterate(detail::erf_roots<typename remove_cv<T>::type, Policy>(p, 1), guess, static_cast<T>(0), tools::max_value<T>(), (policies::digits<T, Policy>() * 2) / 3);
+ result = tools::halley_iterate(detail::erf_roots<typename remove_cv<T>::type, Policy>(p, 1), guess, static_cast<T>(0), tools::max_value<T>(), (policies::digits<T, Policy>() * 2) / 3, max_iter);
}
else
{
- result = tools::halley_iterate(detail::erf_roots<typename remove_cv<T>::type, Policy>(q, -1), guess, static_cast<T>(0), tools::max_value<T>(), (policies::digits<T, Policy>() * 2) / 3);
+ result = tools::halley_iterate(detail::erf_roots<typename remove_cv<T>::type, Policy>(q, -1), guess, static_cast<T>(0), tools::max_value<T>(), (policies::digits<T, Policy>() * 2) / 3, max_iter);
}
+ policies::check_root_iterations("boost::math::erf_inv<%1%>", max_iter, pol);
}
else
{
Modified: trunk/boost/math/special_functions/detail/ibeta_inverse.hpp
==============================================================================
--- trunk/boost/math/special_functions/detail/ibeta_inverse.hpp (original)
+++ trunk/boost/math/special_functions/detail/ibeta_inverse.hpp 2009-10-05 13:36:56 EDT (Mon, 05 Oct 2009)
@@ -795,8 +795,10 @@
// Now iterate, we can use either p or q as the target here
// depending on which is smaller:
//
+ boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
x = boost::math::tools::halley_iterate(
- boost::math::detail::ibeta_roots<T, Policy>(a, b, (p < q ? p : q), (p < q ? false : true)), x, lower, upper, digits);
+ boost::math::detail::ibeta_roots<T, Policy>(a, b, (p < q ? p : q), (p < q ? false : true)), x, lower, upper, digits, max_iter);
+ policies::check_root_iterations("boost::math::ibeta<%1%>(%1%, %1%, %1%)", max_iter, pol);
//
// We don't really want these asserts here, but they are useful for sanity
// checking that we have the limits right, uncomment if you suspect bugs *only*.
Modified: trunk/boost/math/special_functions/detail/igamma_inverse.hpp
==============================================================================
--- trunk/boost/math/special_functions/detail/igamma_inverse.hpp (original)
+++ trunk/boost/math/special_functions/detail/igamma_inverse.hpp 2009-10-05 13:36:56 EDT (Mon, 05 Oct 2009)
@@ -93,7 +93,7 @@
}
template <class T, class Policy>
-T find_inverse_gamma(T a, T p, T q, const Policy& pol)
+T find_inverse_gamma(T a, T p, T q, const Policy& pol, bool* p_has_10_digits)
{
//
// In order to understand what's going on here, you will
@@ -107,6 +107,7 @@
BOOST_MATH_STD_USING
T result;
+ *p_has_10_digits = false;
if(a == 1)
{
@@ -191,6 +192,8 @@
T y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
BOOST_MATH_INSTRUMENT_VARIABLE(result);
+ if(b < 1e-28f)
+ *p_has_10_digits = true;
}
}
else
@@ -218,6 +221,7 @@
if((a >= 500) && (fabs(1 - w / a) < 1e-6))
{
result = w;
+ *p_has_10_digits = true;
BOOST_MATH_INSTRUMENT_VARIABLE(result);
}
else if (p > 0.5)
@@ -269,25 +273,37 @@
}
else
{
- // DiDonato and Morris Eq 35:
- T z = didonato_FN(p, a, w, 0, T(0), pol);
- z = didonato_FN(p, a, z, 2, T(0), pol);
- z = didonato_FN(p, a, z, 2, T(0), pol);
- z = didonato_FN(p, a, z, 3, T(0), pol);
-
- BOOST_MATH_INSTRUMENT_VARIABLE(z);
+ T z = w;
+ T ap1 = a + 1;
+ if(w < 0.15f * ap1)
+ {
+ // DiDonato and Morris Eq 35:
+ T v = log(p) + boost::math::lgamma(ap1, pol);
+ T s = 1;
+ z = exp((v + w) / a);
+ s = boost::math::log1p(z / ap1 * (1 + z / (a + 2)));
+ z = exp((v + z - s) / a);
+ z = exp((v + z - s) / a);
+ s = boost::math::log1p(z / ap1 * (1 + z / (a + 2) * (1 + z / (a + 3))));
+ z = exp((v + z - s) / a);
+ BOOST_MATH_INSTRUMENT_VARIABLE(z);
+ }
- if((z <= 0.01 * (a + 1)) || (z > 0.7 * (a + 1)))
+ if((z <= 0.01 * ap1) || (z > 0.7 * ap1))
{
result = z;
+ if(z <= 0.002 * ap1)
+ *p_has_10_digits = true;
BOOST_MATH_INSTRUMENT_VARIABLE(result);
}
else
{
// DiDonato and Morris Eq 36:
- T zb = didonato_FN(p, a, z, 100, T(1e-4), pol);
- T u = log(p) + boost::math::lgamma(a + 1, pol);
- result = zb * (1 - (a * log(zb) - zb - u + log(didonato_SN(a, z, 100, T(1e-4)))) / (a - zb));
+ T ls = log(didonato_SN(a, z, 100, T(1e-4)));
+ T v = log(p) + boost::math::lgamma(ap1, pol);
+ z = exp((v + z - ls) / a);
+ result = z * (1 - (a * log(z) - z - v + ls) / (a - z));
+
BOOST_MATH_INSTRUMENT_VARIABLE(result);
}
}
@@ -386,7 +402,10 @@
return tools::max_value<T>();
if(p == 0)
return 0;
- T guess = detail::find_inverse_gamma<T>(a, p, 1 - p, pol);
+ bool has_10_digits;
+ T guess = detail::find_inverse_gamma<T>(a, p, 1 - p, pol, &has_10_digits);
+ if((policies::digits<T, Policy>() <= 36) && has_10_digits)
+ return guess;
T lower = tools::min_value<T>();
if(guess <= lower)
guess = tools::min_value<T>();
@@ -397,18 +416,31 @@
// large convergence is slow, so we'll bump it up to full
// precision to prevent premature termination of the root-finding routine.
//
- unsigned digits = (policies::digits<T, Policy>() * 2) / 3;
+ unsigned digits = policies::digits<T, Policy>();
+ if(digits < 30)
+ {
+ digits *= 2;
+ digits /= 3;
+ }
+ else
+ {
+ digits /= 2;
+ digits -= 1;
+ }
if((a < 0.125) && (fabs(gamma_p_derivative(a, guess, pol)) > 1 / sqrt(tools::epsilon<T>())))
digits = policies::digits<T, Policy>() - 2;
//
// Go ahead and iterate:
//
+ boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
guess = tools::halley_iterate(
detail::gamma_p_inverse_func<T, Policy>(a, p, false),
guess,
lower,
tools::max_value<T>(),
- digits);
+ digits,
+ max_iter);
+ policies::check_root_iterations(function, max_iter, pol);
BOOST_MATH_INSTRUMENT_VARIABLE(guess);
if(guess == lower)
guess = policies::raise_underflow_error<T>(function, "Expected result known to be non-zero, but is smaller than the smallest available number.", pol);
@@ -430,7 +462,10 @@
return tools::max_value<T>();
if(q == 1)
return 0;
- T guess = detail::find_inverse_gamma<T>(a, 1 - q, q, pol);
+ bool has_10_digits;
+ T guess = detail::find_inverse_gamma<T>(a, 1 - q, q, pol, &has_10_digits);
+ if((policies::digits<T, Policy>() <= 36) && has_10_digits)
+ return guess;
T lower = tools::min_value<T>();
if(guess <= lower)
guess = tools::min_value<T>();
@@ -440,18 +475,31 @@
// large convergence is slow, so we'll bump it up to full
// precision to prevent premature termination of the root-finding routine.
//
- unsigned digits = (policies::digits<T, Policy>() * 2) / 3;
+ unsigned digits = policies::digits<T, Policy>();
+ if(digits < 30)
+ {
+ digits *= 2;
+ digits /= 3;
+ }
+ else
+ {
+ digits /= 2;
+ digits -= 1;
+ }
if((a < 0.125) && (fabs(gamma_p_derivative(a, guess, pol)) > 1 / sqrt(tools::epsilon<T>())))
digits = policies::digits<T, Policy>();
//
// Go ahead and iterate:
//
+ boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
guess = tools::halley_iterate(
detail::gamma_p_inverse_func<T, Policy>(a, q, true),
guess,
lower,
tools::max_value<T>(),
- digits);
+ digits,
+ max_iter);
+ policies::check_root_iterations(function, max_iter, pol);
if(guess == lower)
guess = policies::raise_underflow_error<T>(function, "Expected result known to be non-zero, but is smaller than the smallest available number.", pol);
return guess;
Modified: trunk/boost/math/special_functions/erf.hpp
==============================================================================
--- trunk/boost/math/special_functions/erf.hpp (original)
+++ trunk/boost/math/special_functions/erf.hpp 2009-10-05 13:36:56 EDT (Mon, 05 Oct 2009)
@@ -197,13 +197,16 @@
//
// We're going to calculate erf:
//
- if(z == 0)
+ if(z < 1e-10)
{
- result = T(0);
- }
- else if(z < 1e-10)
- {
- result = static_cast<T>(z * 1.125f + z * 0.003379167095512573896158903121545171688L);
+ if(z == 0)
+ {
+ result = T(0);
+ }
+ else
+ {
+ result = static_cast<T>(z * 1.125f + z * 0.003379167095512573896158903121545171688L);
+ }
}
else
{
@@ -227,10 +230,11 @@
0.00858571925074406212772L,
0.000370900071787748000569L,
};
- result = z * (Y + tools::evaluate_polynomial(P, z * z) / tools::evaluate_polynomial(Q, z * z));
+ T zz = z * z;
+ result = z * (Y + tools::evaluate_polynomial(P, zz) / tools::evaluate_polynomial(Q, zz));
}
}
- else if((z < 14) || ((z < 28) && invert))
+ else if(invert ? (z < 28) : (z < 5.8f))
{
//
// We'll be calculating erfc:
@@ -425,7 +429,7 @@
result = z * (Y + tools::evaluate_polynomial(P, z * z) / tools::evaluate_polynomial(Q, z * z));
}
}
- else if((z < 110) || ((z < 110) && invert)) // TODO FIXME!!!
+ else if(invert ? (z < 110) : (z < 6.4f))
{
//
// We'll be calculating erfc:
@@ -634,7 +638,7 @@
result = z * (Y + tools::evaluate_polynomial(P, z * z) / tools::evaluate_polynomial(Q, z * z));
}
}
- else if((z < 9) || ((z < 110) && invert))
+ else if(invert ? (z < 110) : (z < 8.65f))
{
//
// We'll be calculating erfc:
Modified: trunk/boost/math/tools/roots.hpp
==============================================================================
--- trunk/boost/math/tools/roots.hpp (original)
+++ trunk/boost/math/tools/roots.hpp 2009-10-05 13:36:56 EDT (Mon, 05 Oct 2009)
@@ -346,6 +346,8 @@
{
// last two steps haven't converged, try bisection:
delta = (delta > 0) ? (result - min) / 2 : (result - max) / 2;
+ if(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;
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk