From c5eda8ad1833530c147ce8969bf9f9dd243b9869 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Thu, 9 Dec 2021 15:40:48 +0000 Subject: [PATCH] Update Eigen NumTraits code. Eigen's requirements have changed in version 3.4. Also improve testing and better support variable precision types. --- include/boost/multiprecision/eigen.hpp | 122 +++++++++++++++++++++++-- include/boost/multiprecision/gmp.hpp | 69 ++++++++++++++ include/boost/multiprecision/mpfr.hpp | 68 ++++++++++++++ test/Jamfile.v2 | 11 ++- test/eigen.hpp | 13 --- test/test_eigen_interop_gmp.cpp | 1 + test/test_eigen_interop_gmp_2.cpp | 15 +++ test/test_eigen_interop_mpc.cpp | 1 + test/test_eigen_interop_mpfr_4.cpp | 15 +++ test/test_eigen_interop_mpfr_5.cpp | 15 +++ test/test_eigen_interop_mpfr_6.cpp | 15 +++ 11 files changed, 321 insertions(+), 24 deletions(-) create mode 100644 test/test_eigen_interop_gmp_2.cpp create mode 100644 test/test_eigen_interop_mpfr_4.cpp create mode 100644 test/test_eigen_interop_mpfr_5.cpp create mode 100644 test/test_eigen_interop_mpfr_6.cpp diff --git a/include/boost/multiprecision/eigen.hpp b/include/boost/multiprecision/eigen.hpp index 9a848eb4..03b7f333 100644 --- a/include/boost/multiprecision/eigen.hpp +++ b/include/boost/multiprecision/eigen.hpp @@ -13,14 +13,18 @@ // Generic Eigen support code: // namespace Eigen { -template -struct NumTraits > + +template +struct NumTraitsImp; + +template +struct NumTraitsImp { - using self_type = boost::multiprecision::number ; - using Real = typename boost::multiprecision::scalar_result_from_possible_complex::type; - using NonInteger = self_type ; // Not correct but we can't do much better?? - using Literal = double ; - using Nested = self_type ; + using self_type = B1; + using Real = typename boost::multiprecision::scalar_result_from_possible_complex::type; + using NonInteger = self_type; // Not correct but we can't do much better?? + using Literal = double; + using Nested = self_type; enum { IsComplex = boost::multiprecision::number_category::value == boost::multiprecision::number_kind_complex, @@ -33,6 +37,7 @@ struct NumTraits > }; static Real epsilon() { + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); return std::numeric_limits::epsilon(); } static Real dummy_precision() @@ -41,14 +46,17 @@ struct NumTraits > } static Real highest() { + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); return (std::numeric_limits::max)(); } static Real lowest() { + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); return (std::numeric_limits::min)(); } static int digits10_imp(const std::integral_constant&) { + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); return std::numeric_limits::digits10; } template @@ -58,13 +66,107 @@ struct NumTraits > } static int digits10() { - return digits10_imp(std::integral_constant::digits10 && (std::numeric_limits::digits10 != INT_MAX) ? true : false > ()); + return digits10_imp(std::integral_constant < bool, std::numeric_limits::digits10 && (std::numeric_limits::digits10 != INT_MAX) ? true : false > ()); + } + static int digits() + { + // return the number of digits in the component type in case Real is complex + // and we have no numeric_limits specialization. + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); + return std::numeric_limits::digits; + } + static int min_exponent() + { + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); + return std::numeric_limits::min_exponent; + } + static int max_exponent() + { + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); + return std::numeric_limits::max_exponent; + } + static Real infinity() + { + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); + return std::numeric_limits::infinity(); + } + static Real quiet_NaN() + { + static_assert(std::numeric_limits::is_specialized, "Eigen's NumTraits instantiated on a type with no numeric_limits support. Are you using a variable precision type?"); + return std::numeric_limits::quiet_NaN(); + } +}; + +template +struct NumTraitsImp : public NumTraitsImp +{ + // + // This version is instantiated when B1 and B2 are different types, this happens for rational/complex/interval + // types, in which case many methods defer to those of the "component type" B2. + // + using self_type = B1; + using Real = typename boost::multiprecision::scalar_result_from_possible_complex::type; + using NonInteger = self_type; // Not correct but we can't do much better?? + using Literal = double; + using Nested = self_type; + enum + { + IsComplex = boost::multiprecision::number_category::value == boost::multiprecision::number_kind_complex, + IsInteger = boost::multiprecision::number_category::value == boost::multiprecision::number_kind_integer, + ReadCost = 1, + AddCost = 4, + MulCost = 8, + IsSigned = std::numeric_limits::is_specialized ? std::numeric_limits::is_signed : true, + RequireInitialization = 1, + }; + static B2 epsilon() + { + return NumTraitsImp::epsilon(); + } + static B2 dummy_precision() + { + return 1000 * epsilon(); + } + static B2 highest() + { + return NumTraitsImp::highest(); + } + static B2 lowest() + { + return NumTraitsImp::lowest(); + } + static int digits10() + { + return NumTraitsImp::digits10(); + } + static int digits() + { + return NumTraitsImp::digits(); + } + static int min_exponent() + { + return NumTraitsImp::min_exponent(); + } + static int max_exponent() + { + return NumTraitsImp::max_exponent(); + } + static B2 infinity() + { + return NumTraitsImp::infinity(); + } + static B2 quiet_NaN() + { + return NumTraitsImp::quiet_NaN(); } }; + +template +struct NumTraits > : public NumTraitsImp, typename boost::multiprecision::number::value_type> +{}; template struct NumTraits > : public NumTraits::result_type> -{ -}; +{}; #define BOOST_MP_EIGEN_SCALAR_TRAITS_DECL(A) \ template \ diff --git a/include/boost/multiprecision/gmp.hpp b/include/boost/multiprecision/gmp.hpp index f022286e..7858ba0b 100644 --- a/include/boost/multiprecision/gmp.hpp +++ b/include/boost/multiprecision/gmp.hpp @@ -3609,4 +3609,73 @@ constexpr float_round_style numeric_limits + struct NumTraitsImp; + + template + struct NumTraitsImp, ExpressionTemplates>, boost::multiprecision::number, ExpressionTemplates> > + { + using self_type = boost::multiprecision::number, ExpressionTemplates>; + using Real = typename boost::multiprecision::scalar_result_from_possible_complex::type; + using NonInteger = self_type; // Not correct but we can't do much better?? + using Literal = double; + using Nested = self_type; + enum + { + IsComplex = boost::multiprecision::number_category::value == boost::multiprecision::number_kind_complex, + IsInteger = boost::multiprecision::number_category::value == boost::multiprecision::number_kind_integer, + ReadCost = 1, + AddCost = 4, + MulCost = 8, + IsSigned = std::numeric_limits::is_specialized ? std::numeric_limits::is_signed : true, + RequireInitialization = 1, + }; + static Real epsilon() + { + return boost::math::tools::epsilon(); + } + static Real dummy_precision() + { + return 1000 * epsilon(); + } + static Real highest() + { + return boost::math::tools::max_value(); + } + static Real lowest() + { + return boost::math::tools::min_value(); + } + static int digits10() + { + return Real::thread_default_precision(); + } + static int digits() + { + return boost::math::tools::digits(); + } + static int min_exponent() + { + return LONG_MIN; + } + static int max_exponent() + { + return LONG_MAX; + } + static Real infinity() + { + return Real(); + } + static Real quiet_NaN() + { + return Real(); + } + }; + +} + + #endif diff --git a/include/boost/multiprecision/mpfr.hpp b/include/boost/multiprecision/mpfr.hpp index adb23de3..80a0735d 100644 --- a/include/boost/multiprecision/mpfr.hpp +++ b/include/boost/multiprecision/mpfr.hpp @@ -3078,6 +3078,74 @@ inline boost::multiprecision::number + struct NumTraitsImp; + + template + struct NumTraitsImp, ExpressionTemplates>, boost::multiprecision::number, ExpressionTemplates>> + { + using self_type = boost::multiprecision::number, ExpressionTemplates>; + using Real = typename boost::multiprecision::scalar_result_from_possible_complex::type; + using NonInteger = self_type; // Not correct but we can't do much better?? + using Literal = double; + using Nested = self_type; + enum + { + IsComplex = boost::multiprecision::number_category::value == boost::multiprecision::number_kind_complex, + IsInteger = boost::multiprecision::number_category::value == boost::multiprecision::number_kind_integer, + ReadCost = 1, + AddCost = 4, + MulCost = 8, + IsSigned = std::numeric_limits::is_specialized ? std::numeric_limits::is_signed : true, + RequireInitialization = 1, + }; + static Real epsilon() + { + return boost::math::tools::epsilon< boost::multiprecision::number, ExpressionTemplates>>(); + } + static Real dummy_precision() + { + return 1000 * epsilon(); + } + static Real highest() + { + return boost::math::tools::max_value, ExpressionTemplates>>(); + } + static Real lowest() + { + return boost::math::tools::min_value, ExpressionTemplates>>(); + } + static int digits10() + { + return Real::thread_default_precision(); + } + static int digits() + { + return boost::math::tools::digits(); + } + static int min_exponent() + { + return mpfr_get_emin(); + } + static int max_exponent() + { + return mpfr_get_emax(); + } + static Real infinity() + { + return std::numeric_limits, ExpressionTemplates>>::infinity(); + } + static Real quiet_NaN() + { + return std::numeric_limits, ExpressionTemplates>>::quiet_NaN(); + } + }; + +} + namespace std { // diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 9ee8bbf5..4144d9e0 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1171,11 +1171,20 @@ test-suite misc : [ run test_eigen_interop_mpfr_1.cpp mpfr gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_mpfr : : no ] ] [ run test_eigen_interop_mpfr_2.cpp mpfr gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_mpfr : : no ] ] [ run test_eigen_interop_mpfr_3.cpp mpfr gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_mpfr : : no ] ] + [ run test_eigen_interop_mpfr_4.cpp mpfr gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_mpfr : : no ] ] + [ run test_eigen_interop_mpfr_5.cpp mpfr gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_mpfr : : no ] ] + [ run test_eigen_interop_mpfr_6.cpp mpfr gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_mpfr : : no ] ] [ run test_eigen_interop_gmp.cpp gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_gmp : : no ] ] + [ run test_eigen_interop_gmp_2.cpp gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_gmp : : no ] ] [ run test_eigen_interop_mpc.cpp mpc mpfr gmp : : : release [ check-target-builds ../config//has_eigen : : no ] [ check-target-builds ../config//has_mpc : : no ] ] - [ run git_issue_393.cpp : : : release [ check-target-builds ../config//has_eigen : : no ] ] + [ compile git_issue_393.cpp : release [ check-target-builds ../config//has_eigen : : no ] ] ; +alias eigen_tests : test_eigen_interop_cpp_int test_eigen_interop_cpp_dec_float test_eigen_interop_cpp_dec_float_2 test_eigen_interop_cpp_dec_float_3 +test_eigen_interop_cpp_bin_float_1 test_eigen_interop_cpp_bin_float_2 test_eigen_interop_cpp_bin_float_3 test_eigen_interop_mpfr_1 +test_eigen_interop_mpfr_2 test_eigen_interop_mpfr_3 test_eigen_interop_mpfr_4 test_eigen_interop_mpfr_5 test_eigen_interop_mpfr_6 +test_eigen_interop_gmp test_eigen_interop_gmp_2 test_eigen_interop_mpc git_issue_393 ; + # # This take too long to run as a regular part of the tests: diff --git a/test/eigen.hpp b/test/eigen.hpp index c01b949a..be314369 100644 --- a/test/eigen.hpp +++ b/test/eigen.hpp @@ -549,15 +549,6 @@ void test_float_type() example15(); example16(); example17(); - /* - example18(); - example19(); - example20(); - example21(); - example22(); - example23(); - example24(); - */ } template @@ -573,10 +564,6 @@ void test_float_type_2() example19(); example20(); example21(); - - //example22(); - //example23(); - //example24(); } template diff --git a/test/test_eigen_interop_gmp.cpp b/test/test_eigen_interop_gmp.cpp index 4cb2385b..ab387d99 100644 --- a/test/test_eigen_interop_gmp.cpp +++ b/test/test_eigen_interop_gmp.cpp @@ -12,5 +12,6 @@ int main() using namespace boost::multiprecision; test_integer_type(); test_integer_type(); + return 0; } diff --git a/test/test_eigen_interop_gmp_2.cpp b/test/test_eigen_interop_gmp_2.cpp new file mode 100644 index 00000000..e6d85b38 --- /dev/null +++ b/test/test_eigen_interop_gmp_2.cpp @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 John Maddock. Distributed under 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) + +#include + +#include "eigen.hpp" + +int main() +{ + test_float_type(); + test_float_type(); + return 0; +} diff --git a/test/test_eigen_interop_mpc.cpp b/test/test_eigen_interop_mpc.cpp index 6ac9966c..38245f0c 100644 --- a/test/test_eigen_interop_mpc.cpp +++ b/test/test_eigen_interop_mpc.cpp @@ -11,5 +11,6 @@ int main() { using namespace boost::multiprecision; test_complex_type(); + test_complex_type(); return 0; } diff --git a/test/test_eigen_interop_mpfr_4.cpp b/test/test_eigen_interop_mpfr_4.cpp new file mode 100644 index 00000000..5e2b55c7 --- /dev/null +++ b/test/test_eigen_interop_mpfr_4.cpp @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 John Maddock. Distributed under 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) + +#include + +#include "eigen.hpp" + +int main() +{ + using namespace boost::multiprecision; + test_float_type(); + return 0; +} diff --git a/test/test_eigen_interop_mpfr_5.cpp b/test/test_eigen_interop_mpfr_5.cpp new file mode 100644 index 00000000..c3c8f6a9 --- /dev/null +++ b/test/test_eigen_interop_mpfr_5.cpp @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 John Maddock. Distributed under 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) + +#include + +#include "eigen.hpp" + +int main() +{ + using namespace boost::multiprecision; + test_float_type_2(); + return 0; +} diff --git a/test/test_eigen_interop_mpfr_6.cpp b/test/test_eigen_interop_mpfr_6.cpp new file mode 100644 index 00000000..45ad64ff --- /dev/null +++ b/test/test_eigen_interop_mpfr_6.cpp @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2018 John Maddock. Distributed under 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) + +#include + +#include "eigen.hpp" + +int main() +{ + using namespace boost::multiprecision; + test_float_type_3(); + return 0; +}