Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving DE performance Part 2 #722

Merged
merged 8 commits into from
Jan 16, 2022
52 changes: 32 additions & 20 deletions include/boost/math/quadrature/detail/exp_sinh_detail.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,34 +158,52 @@ auto exp_sinh_detail<Real, Policy>::integrate(const F& f, Real* error, Real* L1,
using boost::math::constants::half;
using boost::math::constants::half_pi;

// This provided a nice error message for real valued integrals, but it's super awkward for complex-valued integrals:
/*K y_max = f(tools::max_value<Real>());
if(abs(y_max) > tools::epsilon<Real>() || !(boost::math::isfinite)(y_max))
{
K val = abs(y_max);
return static_cast<K>(policies::raise_domain_error(function, "The function you are trying to integrate does not go to zero at infinity, and instead evaluates to %1%", val, Policy()));
}*/
// This provided a nice error message for real valued integrals, but it's super awkward for complex-valued integrals:
/*K y_max = f(tools::max_value<Real>());
if(abs(y_max) > tools::epsilon<Real>() || !(boost::math::isfinite)(y_max))
{
K val = abs(y_max);
return static_cast<K>(policies::raise_domain_error(function, "The function you are trying to integrate does not go to zero at infinity, and instead evaluates to %1%", val, Policy()));
}*/

//std::cout << std::setprecision(5*std::numeric_limits<Real>::digits10);
//std::cout << std::setprecision(5*std::numeric_limits<Real>::digits10);

// Get the party started with two estimates of the integral:
Real min_abscissa{ 0 }, max_abscissa{ boost::math::tools::max_value<Real>() };
K I0 = 0;
Real L1_I0 = 0;
for(size_t i = 0; i < m_abscissas[0].size(); ++i)
{
K y = f(m_abscissas[0][i]);
K I0_last = I0;
I0 += y*m_weights[0][i];
L1_I0 += abs(y)*m_weights[0][i];
if ((I0_last == I0) && (abs(I0) != 0))
{
max_abscissa = m_abscissas[0][i];
break;
}
}

//std::cout << "First estimate : " << I0 << std::endl;
K I1 = I0;
Real L1_I1 = L1_I0;
for (size_t i = 0; i < m_abscissas[1].size(); ++i)
bool have_first_j = false;
std::size_t first_j = 0;
for (size_t i = 0; (i < m_abscissas[1].size()) && (m_abscissas[1][i] < max_abscissa); ++i)
{
K y = f(m_abscissas[1][i]);
K I1_last = I1;
I1 += y*m_weights[1][i];
L1_I1 += abs(y)*m_weights[1][i];
if (!have_first_j && (I1_last == I1))
{
// No change to the sum, disregard these values on the LHS:
min_abscissa = m_abscissas[1][i];
first_j = i;
}
else
have_first_j = true;
}

I1 *= half<Real>();
Expand All @@ -208,24 +226,18 @@ auto exp_sinh_detail<Real, Policy>::integrate(const F& f, Real* error, Real* L1,
auto abscissas_row = get_abscissa_row(i);
auto weight_row = get_weight_row(i);

first_j = first_j == 0 ? 0 : 2 * first_j - 1; // appoximate location to start looking for lowest meaningful abscissa value
Real abterm1 = 1;
Real eps = tools::epsilon<Real>()*L1_I1;
for(size_t j = 0; j < m_weights[i].size(); ++j)
std::size_t j = first_j;
while (abscissas_row[j] < min_abscissa)
++j;
for(; (j < m_weights[i].size()) && (abscissas_row[j] < max_abscissa); ++j)
{
Real x = abscissas_row[j];
K y = f(x);
sum += y*weight_row[j];
Real abterm0 = abs(y)*weight_row[j];
absum += abterm0;

// We require two consecutive terms to be < eps in case we hit a zero of f.
// Numerical experiments indicate that we should start this check at ~30 for floats,
// ~60 for doubles, and ~100 for long doubles.
// However, starting the check at x = 10 rather than x = 100 will only save two function evaluations.
if (x > static_cast<Real>(100) && abterm0 < eps && abterm1 < eps)
{
break;
}
abterm1 = abterm0;
}

Expand Down
58 changes: 46 additions & 12 deletions include/boost/math/quadrature/detail/tanh_sinh_detail.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,29 +144,33 @@ class tanh_sinh_detail
{
using std::tanh;
using std::sinh;
return tanh(constants::half_pi<Real>()*sinh(t));
using boost::math::constants::half_pi;
return tanh(half_pi<Real>()*sinh(t));
}
static inline Real weight_at_t(const Real& t)
{
using std::cosh;
using std::sinh;
Real cs = cosh(constants::half_pi<Real>() * sinh(t));
return constants::half_pi<Real>() * cosh(t) / (cs * cs);
using boost::math::constants::half_pi;
Real cs = cosh(half_pi<Real>() * sinh(t));
return half_pi<Real>() * cosh(t) / (cs * cs);
}
static inline Real abscissa_complement_at_t(const Real& t)
{
using std::cosh;
using std::exp;
using std::sinh;
Real u2 = constants::half_pi<Real>() * sinh(t);
using boost::math::constants::half_pi;
Real u2 = half_pi<Real>() * sinh(t);
return 1 / (exp(u2) *cosh(u2));
}
static inline Real t_from_abscissa_complement(const Real& x)
{
using std::log;
using std::sqrt;
using boost::math::constants::pi;
Real l = log(2-x) - log(x);
return log((sqrt(l * l + constants::pi<Real>() * constants::pi<Real>()) + l) / constants::pi<Real>());
return log((sqrt(l * l + pi<Real>() * pi<Real>()) + l) / pi<Real>());
};


Expand Down Expand Up @@ -232,21 +236,41 @@ decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) tanh_sin
//
// Check for non-finite values at the end points:
//
result_type yp, ym;
result_type yp, ym, tail_tolerance((std::max)(boost::math::tools::epsilon<Real>(), Real(tolerance * tolerance)));
do
{
yp = f(-1 - m_abscissas[0][max_left_position], m_abscissas[0][max_left_position]);
if ((boost::math::isfinite)(yp))
break;
--max_left_position;
} while (m_abscissas[0][max_left_position] < 0);
//
// Also remove points which are insignificant or zero:
//
while (max_left_position > 1)
{
if (abs(yp * m_weights[0][max_left_position]) > abs(L1_I0 * tail_tolerance))
break;
--max_left_position;
yp = f(-1 - m_abscissas[0][max_left_position], m_abscissas[0][max_left_position]);
}
//
// Over again for the right hand side:
//
do
{
ym = f(1 + m_abscissas[0][max_right_position], -m_abscissas[0][max_right_position]);
if ((boost::math::isfinite)(ym))
break;
--max_right_position;
} while (m_abscissas[0][max_right_position] < 0);
while (max_right_position > 1)
{
if (abs(ym * m_weights[0][max_right_position]) > abs(L1_I0 * tail_tolerance))
break;
--max_right_position;
ym = f(1 + m_abscissas[0][max_right_position], -m_abscissas[0][max_right_position]);
}

if ((max_left_position == 0) && (max_right_position == 0))
{
Expand Down Expand Up @@ -350,6 +374,9 @@ decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) tanh_sin
max_left_position -= 2;
--max_left_index;
} while (abscissa_row[max_left_index] < 0);
bool truncate_left(false), truncate_right(false);
if (abs(L1_I1 * tail_tolerance) > abs(yp * weight_row[max_left_index]))
truncate_left = true;
do
{
ym = f(1 + abscissa_row[max_right_index], -abscissa_row[max_right_index]);
Expand All @@ -358,6 +385,8 @@ decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) tanh_sin
--max_right_index;
max_right_position -= 2;
} while (abscissa_row[max_right_index] < 0);
if (abs(L1_I1 * tail_tolerance) > abs(ym * weight_row[max_right_index]))
truncate_right = true;

sum += yp * weight_row[max_left_index] + ym * weight_row[max_right_index];
absum += abs(yp * weight_row[max_left_index]) + abs(ym * weight_row[max_right_index]);
Expand Down Expand Up @@ -408,10 +437,10 @@ decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) tanh_sin

I1 += sum*h;
L1_I1 += absum*h;

++k;
Real last_err = err;
err = abs(I0 - I1);
// std::cout << "Estimate: " << I1 << " Error estimate at level " << k << " = " << err << std::endl;

if (!(boost::math::isfinite)(I1))
{
Expand Down Expand Up @@ -462,7 +491,7 @@ decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) tanh_sin
// parameters. We could keep hunting until we find something, but that would handicap
// integrals which really are zero.... so a compromise then!
//
if (err <= abs(tolerance*L1_I1))
if ((err <= abs(tolerance*L1_I1)) && (k >= 4))
{
//
// A quick sanity check: have we at some point narrowed our boundaries as a result
Expand All @@ -476,22 +505,27 @@ decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) tanh_sin
ym = f(-1 - abscissa_row[max_left_index - 1], abscissa_row[max_left_index - 1]) * weight_row[max_left_index - 1];
if (abs(yp) > abs(ym))
{
return policies::raise_evaluation_error(function, "The tanh_sinh quadrature evaluated your function at a singular point and got %1%. Inetgration bounds were automatically narrowed, but the integral was found to be increasing at the new endpoint. Please check your function, and consider providing a 2-argument functor.", I1, Policy());
return policies::raise_evaluation_error(function, "The tanh_sinh quadrature evaluated your function at a singular point and got %1%. Integration bounds were automatically narrowed, but the integral was found to be increasing at the new endpoint. Please check your function, and consider providing a 2-argument functor.", I1, Policy());
}
}
if ((max_right_index < abscissa_row.size() - 1) && (abs(abscissa_row[max_right_index + 1]) > right_min_complement))
{
yp = f(-1 - abscissa_row[max_right_index], abscissa_row[max_right_index]) * weight_row[max_right_index];
ym = f(-1 - abscissa_row[max_right_index - 1], abscissa_row[max_right_index - 1]) * weight_row[max_right_index - 1];
yp = f(1 + abscissa_row[max_right_index], -abscissa_row[max_right_index]) * weight_row[max_right_index];
ym = f(1 + abscissa_row[max_right_index - 1], -abscissa_row[max_right_index - 1]) * weight_row[max_right_index - 1];
if (abs(yp) > abs(ym))
{
return policies::raise_evaluation_error(function, "The tanh_sinh quadrature evaluated your function at a singular point and got %1%. Inetgration bounds were automatically narrowed, but the integral was found to be increasing at the new endpoint. Please check your function, and consider providing a 2-argument functor.", I1, Policy());
return policies::raise_evaluation_error(function, "The tanh_sinh quadrature evaluated your function at a singular point and got %1%. Integration bounds were automatically narrowed, but the integral was found to be increasing at the new endpoint. Please check your function, and consider providing a 2-argument functor.", I1, Policy());
}
}
err += endpoint_error;
break;
}

if (truncate_left)
--max_left_position;
if (truncate_right)
--max_right_position;

}
if (error)
{
Expand Down
6 changes: 6 additions & 0 deletions test/tanh_sinh_quadrature_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,12 @@ void test_ca()
auto f7 = [](const Real& t) { return sqrt(tan(t)); };
Q = integrator.integrate(f7, (Real) 0 , (Real) half_pi<Real>(), get_convergence_tolerance<Real>(), &error, &L1);
Q_expected = pi<Real>()/root_two<Real>();
//
// Slightly higher tolerance for type float, this marginal change was
// caused by no more than changing the order in which the terms are summed:
//
if (std::is_same<Real, float>::value)
tol *= 1.5;
BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
BOOST_CHECK_CLOSE_FRACTION(L1, Q_expected, tol);

Expand Down
2 changes: 1 addition & 1 deletion test/test_lambert_w_integrals_float.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ void test_integrals()
return lambert_w0<Real>(z);
};
Real z = ts.integrate(f, a, b); // OK without any decltype(f)
BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e<Real>() - 1, tol);
BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e<Real>() - 1, tol * 3);
}
{
// Integrate for function lambert_W0(z/(z sqrt(z)).
Expand Down