Fixes ibeta for large arguments#1363
Conversation
|
@jzmaddock is there a way to see if the continued fraction is converging quickly or not? Maybe a ratio of subsequent terms or a change in the value of the continued fraction? In this spirit, the easiest think would just be to add a try/catch statement as done here try
{
fract = ibeta_fraction2(a, b, x, y, pol, normalised, p_derivative);
}
// If series converges slowly, fall back to erf approximation
catch (boost::math::evaluation_error& e)
{
fract = ibeta_large_ab(a, b, x, y, invert, normalised, pol);
}Maybe this will have unintended consequences though? |
Non-programatically you can define
We test and run on a number of BOOST_MATH_GPU_ENABLED inline T ibeta_fraction2(T a, T b, T x, T y, const Policy& pol, bool normalised, T* p_derivative, boost::math::uintmax_t iters = nullptr);Since uintmax_t iters;
fract = ibeta_fraction2(a, b, x, y, pol, normalised, p_derivative, &iters);
If (too many iters)
{
fract = ibeta_large_ab(a, b, x, y, invert, normalised, pol);
}Defaulting to, and checking the |
The issue is that before returning, boost::math::policies::check_series_iterations<T>("boost::math::ibeta", max_terms, pol);which throws the evaluation error. I think that your solution would work, but then we would have to get rid of the check above in Maybe there's an easier solution I'm not seeing though. Here's the function in question: template <class T, class Policy>
BOOST_MATH_GPU_ENABLED inline T ibeta_fraction2(T a, T b, T x, T y, const Policy& pol, bool normalised, T* p_derivative)
{
typedef typename lanczos::lanczos<T, Policy>::type lanczos_type;
BOOST_MATH_STD_USING
T result = ibeta_power_terms(a, b, x, y, lanczos_type(), normalised, pol);
if(p_derivative)
{
*p_derivative = result;
BOOST_MATH_ASSERT(*p_derivative >= 0);
}
if(result == 0)
return result;
ibeta_fraction2_t<T> f(a, b, x, y);
boost::math::uintmax_t max_terms = boost::math::policies::get_max_series_iterations<Policy>();
T fract = boost::math::tools::continued_fraction_b(f, boost::math::policies::get_epsilon<T, Policy>(), max_terms);
boost::math::policies::check_series_iterations<T>("boost::math::ibeta", max_terms, pol); // Want to catch this error!
BOOST_MATH_INSTRUMENT_VARIABLE(fract);
BOOST_MATH_INSTRUMENT_VARIABLE(result);
return result / fract;
} |
|
In this case non-convergence isn't exceptional, it's somewhat expected right? Passing a modified policy to template <class T, class Policy>
BOOST_MATH_GPU_ENABLED inline T ibeta_fraction2(T a, T b, T x, T y, const Policy& pol, bool normalised, T* p_derivative, uintmax_t* iters = nullptr)
{
typedef typename lanczos::lanczos<T, Policy>::type lanczos_type;
BOOST_MATH_STD_USING
T result = ibeta_power_terms(a, b, x, y, lanczos_type(), normalised, pol);
if(p_derivative)
{
*p_derivative = result;
BOOST_MATH_ASSERT(*p_derivative >= 0);
}
if(result == 0)
return result;
ibeta_fraction2_t<T> f(a, b, x, y);
boost::math::uintmax_t max_terms = boost::math::policies::get_max_series_iterations<Policy>();
T fract = boost::math::tools::continued_fraction_b(f, boost::math::policies::get_epsilon<T, Policy>(), max_terms);
boost::math::policies::check_series_iterations<T>("boost::math::ibeta", max_terms, pol); // Nothing happens
if(iters && max_iter >= policies::get_max_series_iterations<Policy>()) *iters = max_iter;
BOOST_MATH_INSTRUMENT_VARIABLE(fract);
BOOST_MATH_INSTRUMENT_VARIABLE(result);
return result / fract;
} |
I think that should work! I'm not very familiar with policies though. How would I modify the policy to |
using ignore_iterations_policy = typename boost::math::policies::normalise<
Policy,
boost::math::policies::max_series_iterations_error<boost::math::policies::ignore_error>
>::type;
ignore_iterations_policy new_policy;And then use the |
Closes #1361 and scipy/scipy#24566.
Only the expansion for$\eta$ has been added. When to use the erf expansion and relevant tests need to be added.