1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
|
// Copyright Paul A. Bristow 2016, 2017, 2018.
// Copyright John Maddock 2016.
// 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)
// test_lambert_w_integrals.cpp
//! \brief quadrature tests that cover the whole range of the Lambert W0 function.
#include <boost/config.hpp> // for BOOST_MSVC definition etc.
#include <boost/version.hpp> // for BOOST_MSVC versions.
#include <climits>
#if defined(BOOST_HAS_FLOAT128) && (LDBL_MANT_DIG > 100)
//
// Mixing __float128 and long double results in:
// error: __float128 and long double cannot be used in the same expression
// whenever long double is a [possibly quasi-] quad precision type.
//
#undef BOOST_HAS_FLOAT128
#endif
#ifdef BOOST_HAS_FLOAT128
// Boost macros
#define BOOST_TEST_MAIN
#define BOOST_LIB_DIAGNOSTIC "on" // Report library file details.
#include <boost/test/included/unit_test.hpp> // Boost.Test
#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/array.hpp>
#include <boost/type_traits/is_constructible.hpp>
#include <boost/multiprecision/float128.hpp>
#include <boost/math/special_functions/fpclassify.hpp> // isnan, isfinite.
#include <boost/math/special_functions/next.hpp> // float_next, float_prior
using boost::math::float_next;
using boost::math::float_prior;
#include <boost/math/special_functions/ulp.hpp> // ulp
#include <boost/math/tools/test_value.hpp> // for create_test_value and macro BOOST_MATH_TEST_VALUE.
#include <boost/math/policies/policy.hpp>
using boost::math::policies::digits2;
using boost::math::policies::digits10;
#include <boost/math/special_functions/lambert_w.hpp> // For Lambert W lambert_w function.
using boost::math::lambert_wm1;
using boost::math::lambert_w0;
#include <limits>
#include <cmath>
#include <typeinfo>
#include <iostream>
#include <type_traits>
#include <exception>
std::string show_versions(void);
// Added code and test for Integral of the Lambert W function: by Nick Thompson.
// https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals
#include <boost/math/constants/constants.hpp> // for integral tests.
#include <boost/math/quadrature/tanh_sinh.hpp> // for integral tests.
#include <boost/math/quadrature/exp_sinh.hpp> // for integral tests.
using boost::math::policies::policy;
using boost::math::policies::make_policy;
// using statements needed for changing error handling policy.
using boost::math::policies::evaluation_error;
using boost::math::policies::domain_error;
using boost::math::policies::overflow_error;
using boost::math::policies::ignore_error;
using boost::math::policies::throw_on_error;
typedef policy<
domain_error<throw_on_error>,
overflow_error<ignore_error>
> no_throw_policy;
// Assumes that function has a throw policy, for example:
// NOT lambert_w0<T>(1 / (x * x), no_throw_policy());
// Error in function boost::math::quadrature::exp_sinh<double>::integrate:
// The exp_sinh quadrature evaluated your function at a singular point and resulted in inf.
// Please ensure your function evaluates to a finite number of its entire domain.
template <typename T>
T debug_integration_proc(T x)
{
T result; // warning C4701: potentially uninitialized local variable 'result' used
// T result = 0 ; // But result may not be assigned below?
try
{
// Assign function call to result in here...
if (x <= sqrt(boost::math::tools::min_value<T>()) )
{
result = 0;
}
else
{
result = lambert_w0<T>(1 / (x * x));
}
// result = lambert_w0<T>(1 / (x * x), no_throw_policy()); // Bad idea, less helpful diagnostic message is:
// Error in function boost::math::quadrature::exp_sinh<double>::integrate:
// The exp_sinh quadrature evaluated your function at a singular point and resulted in inf.
// Please ensure your function evaluates to a finite number of its entire domain.
} // try
catch (const std::exception& e)
{
std::cout << "Exception " << e.what() << std::endl;
// set breakpoint here:
std::cout << "Unexpected exception thrown in integration code at abscissa (x): " << x << "." << std::endl;
if (!std::isfinite(result))
{
// set breakpoint here:
std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl;
}
if (std::isnan(result))
{
// set breakpoint here:
std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl;
}
} // catch
return result;
} // T debug_integration_proc(T x)
template<class Real>
void test_integrals()
{
// Integral of the Lambert W function:
// https://en.wikipedia.org/wiki/Lambert_W_function
using boost::math::quadrature::tanh_sinh;
using boost::math::quadrature::exp_sinh;
// file:///I:/modular-boost/libs/math/doc/html/math_toolkit/quadrature/double_exponential/de_tanh_sinh.html
using std::sqrt;
std::cout << "Integration of type " << typeid(Real).name() << std::endl;
Real tol = std::numeric_limits<Real>::epsilon();
{ // // Integrate for function lambert_W0(z);
tanh_sinh<Real> ts;
Real a = 0;
Real b = boost::math::constants::e<Real>();
auto f = [](Real z)->Real
{
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);
}
{
// Integrate for function lambert_W0(z/(z sqrt(z)).
exp_sinh<Real> es;
auto f = [](Real z)->Real
{
return lambert_w0<Real>(z)/(z * sqrt(z));
};
Real z = es.integrate(f); // OK
BOOST_CHECK_CLOSE_FRACTION(z, 2 * boost::math::constants::root_two_pi<Real>(), tol);
}
{
// Integrate for function lambert_W0(1/z^2).
exp_sinh<Real> es;
//const Real sqrt_min = sqrt(boost::math::tools::min_value<Real>()); // 1.08420217e-19 fo 32-bit float.
// error C3493: 'sqrt_min' cannot be implicitly captured because no default capture mode has been specified
auto f = [](Real z)->Real
{
if (z <= sqrt(boost::math::tools::min_value<Real>()) )
{ // Too small would underflow z * z and divide by zero to overflow 1/z^2 for lambert_w0 z parameter.
return static_cast<Real>(0);
}
else
{
return lambert_w0<Real>(1 / (z * z)); // warning C4756: overflow in constant arithmetic, even though cannot happen.
}
};
Real z = es.integrate(f);
BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::root_two_pi<Real>(), tol);
}
} // template<class Real> void test_integrals()
BOOST_AUTO_TEST_CASE( integrals )
{
std::cout << "Macro BOOST_MATH_LAMBERT_W0_INTEGRALS is defined." << std::endl;
BOOST_TEST_MESSAGE("\nTest Lambert W0 integrals.");
try
{
// using statements needed to change precision policy.
using boost::math::policies::policy;
using boost::math::policies::make_policy;
using boost::math::policies::precision;
using boost::math::policies::digits2;
using boost::math::policies::digits10;
// using statements needed for changing error handling policy.
using boost::math::policies::evaluation_error;
using boost::math::policies::domain_error;
using boost::math::policies::overflow_error;
using boost::math::policies::ignore_error;
using boost::math::policies::throw_on_error;
/*
typedef policy<
domain_error<throw_on_error>,
overflow_error<ignore_error>
> no_throw_policy;
// Experiment with better diagnostics.
typedef float Real;
Real inf = std::numeric_limits<Real>::infinity();
Real max = (std::numeric_limits<Real>::max)();
std::cout.precision(std::numeric_limits<Real>::max_digits10);
//std::cout << "lambert_w0(inf) = " << lambert_w0(inf) << std::endl; // lambert_w0(inf) = 1.79769e+308
std::cout << "lambert_w0(inf, throw_policy()) = " << lambert_w0(inf, no_throw_policy()) << std::endl; // inf
std::cout << "lambert_w0(max) = " << lambert_w0(max) << std::endl; // lambert_w0(max) = 703.227
//std::cout << lambert_w0(inf) << std::endl; // inf - will throw.
std::cout << "lambert_w0(0) = " << lambert_w0(0.) << std::endl; // 0
std::cout << "lambert_w0(std::numeric_limits<Real>::denorm_min()) = " << lambert_w0(std::numeric_limits<Real>::denorm_min()) << std::endl; // 4.94066e-324
std::cout << "lambert_w0(std::numeric_limits<Real>::min()) = " << lambert_w0((std::numeric_limits<Real>::min)()) << std::endl; // 2.22507e-308
// Approximate the largest lambert_w you can get for type T?
float max_w_f = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits<float>::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162.
std::cout << "w max_f " << max_w_f << std::endl; // 84.2879
Real max_w = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits<Real>::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162.
std::cout << "w max " << max_w << std::endl; // 703.227
std::cout << "lambert_w0(7.2416706213544837e-163) = " << lambert_w0(7.2416706213544837e-163) << std::endl; //
std::cout << "test integral 1/z^2" << std::endl;
std::cout << "ULP = " << boost::math::ulp(1., policy<digits2<> >()) << std::endl; // ULP = 2.2204460492503131e-16
std::cout << "ULP = " << boost::math::ulp(1e-10, policy<digits2<> >()) << std::endl; // ULP = 2.2204460492503131e-16
std::cout << "ULP = " << boost::math::ulp(1., policy<digits2<11> >()) << std::endl; // ULP = 2.2204460492503131e-16
std::cout << "epsilon = " << std::numeric_limits<Real>::epsilon() << std::endl; //
std::cout << "sqrt(max) = " << sqrt(boost::math::tools::max_value<float>() ) << std::endl; // sqrt(max) = 1.8446742974197924e+19
std::cout << "sqrt(min) = " << sqrt(boost::math::tools::min_value<float>() ) << std::endl; // sqrt(min) = 1.0842021724855044e-19
// Demo debug version.
Real tol = std::numeric_limits<Real>::epsilon();
Real x;
{
using boost::math::quadrature::exp_sinh;
exp_sinh<Real> es;
// Function to be integrated, lambert_w0(1/z^2).
//auto f = [](Real z)->Real
//{ // Naive - no protection against underflow and subsequent divide by zero.
// return lambert_w0<Real>(1 / (z * z));
//};
// Diagnostic is:
// Error in function boost::math::lambert_w0<Real>: Expected a finite value but got inf
auto f = [](Real z)->Real
{ // Debug with diagnostics for underflow and subsequent divide by zero and other bad things.
return debug_integration_proc(z);
};
// Exception Error in function boost::math::lambert_w0<double>: Expected a finite value but got inf.
// Unexpected exception thrown in integration code at abscissa: 7.2416706213544837e-163.
// Unexpected exception thrown in integration code at abscissa (x): 3.478765835953569e-23.
x = es.integrate(f);
std::cout << "es.integrate(f) = " << x << std::endl;
BOOST_CHECK_CLOSE_FRACTION(x, boost::math::constants::root_two_pi<Real>(), tol);
// root_two_pi<double = 2.506628274631000502
}
*/
test_integrals<boost::multiprecision::float128>();
}
catch (std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
}
#else
int main() { return 0; }
#endif
|