headmyshoulder / odeint-v2

odeint - solving ordinary differential equations in c++ v2
http://headmyshoulder.github.com/odeint-v2/
Other
337 stars 102 forks source link

boost::multi precision and boost::find_if fails with odeint::make_adaptive_range and (at least) Boost 1.70 #245

Open ds283 opened 4 years ago

ds283 commented 4 years ago

Summary

For some years I have been successfully using odeint-v2 and boost::multiprecision for extended-precision arithmetic.

Recently MacPorts upgraded their version of Boost from 1.66 to 1.70. Since this happened, code using boost::find_if in conjunction with a boost::multiprecision state type will no longer compile.

I've attached a minimal example exhibiting the error, which is the standard find_crossing.cpp example generalised to use boost::multiprecision::cpp_dec_float_50.

The same error occurs using the mpfr library as the backend to boost::multiprecision::number, which is the arrangement used in my production code. Also, in production we are using odeint-v2 obtained direct from this GitHub repository at commit hash db8b39ae rather than the version bundled with Boost 1.70. None of these details seem to make a difference.

On compilation the example attached above gives

$ clang++ -std=c++11 -ftemplate-backtrace-limit=0 -I/opt/local/include find_if_multiprecision.cpp
In file included from find_if_multiprecision.cpp:24:
In file included from /opt/local/include/boost/numeric/odeint.hpp:27:
In file included from /opt/local/include/boost/numeric/odeint/stepper/euler.hpp:25:
In file included from /opt/local/include/boost/numeric/odeint/algebra/range_algebra.hpp:28:
In file included from /opt/local/include/boost/numeric/odeint/algebra/norm_result_type.hpp:20:
/opt/local/include/boost/numeric/odeint/algebra/detail/extract_value_type.hpp:47:68: error: no type named 'type' in 'boost::numeric::odeint::detail::extract_value_type<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<50, int, void>, boost::multiprecision::et_on>, void>'
    typedef typename extract_value_type< typename S::value_type >::type type;

Backtrace details

As far as I can tell, this is the error I am encountering in production code. Most of the template backtrace is identical:

/opt/local/include/boost/numeric/odeint/algebra/detail/extract_value_type.hpp:47:68: error: no type named 'type' in 'boost::numeric::odeint::detail::extract_value_type<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<50, int, void>, boost::multiprecision::et_on>, void>'
    typedef typename extract_value_type< typename S::value_type >::type type;
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
...
    typedef typename extract_value_type< typename S::value_type >::type type;
                     ^
/opt/local/include/boost/numeric/odeint/algebra/norm_result_type.hpp:28:30: note: in instantiation of template class ...
    typedef typename detail::extract_value_type< S >::type type;
                             ^
/opt/local/include/boost/numeric/odeint/algebra/array_algebra.hpp:276:21: note: in instantiation of template class ...
        return algebra.norm_inf( x_err );
                       ^
/opt/local/include/boost/numeric/odeint/stepper/controlled_runge_kutta.hpp:768:50: note: in instantiation of function template specialization ...
        value_type max_rel_err = m_error_checker.error( m_stepper.algebra() , in , dxdt_in , m_xerr.m_v , dt );
                                                 ^
/opt/local/include/boost/numeric/odeint/stepper/dense_output_runge_kutta.hpp:338:29: note: in instantiation of function template specialization ...
            res = m_stepper.try_step( system , get_current_state() , get_current_deriv() , m_t ,
                            ^
/opt/local/include/boost/numeric/odeint/iterator/impl/adaptive_iterator_impl.hpp:230:25: note: in instantiation of function template specialization ...
                stepper.do_step( this->m_system );
                        ^
/opt/local/include/boost/iterator/iterator_facade.hpp:556:13: note: in instantiation of member function ...
          f.increment();
            ^
/opt/local/include/boost/iterator/iterator_facade.hpp:666:35: note: in instantiation of function template specialization ...
            iterator_core_access::increment(this->derived());
                                  ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:1011:31: note: in instantiation of member function ...
    for (; __first != __last; ++__first)

In my production code I have a slightly longer backtrace that includes

/opt/local/include/boost/iterator/iterator_facade.hpp:666:35: note: in instantiation of function template specialization 'boost::iterators::iterator_core_access::increment<boost::numeric::odeint::adaptive_time_iterator ... requested here
            iterator_core_access::increment(this->derived());
                                  ^
/opt/local/include/boost/range/concepts.hpp:138:17: note: in instantiation of member function 'boost::iterators::detail::iterator_facade_base<boost::numeric::odeint::adaptive_time_iterator ... requested here
                ++i;
                ^
/opt/local/include/boost/concept/usage.hpp:16:43: note: in instantiation of member function 'boost::range_detail::IncrementableIteratorConcept<boost::numeric::odeint::adaptive_time_iterator ... requested here
    ~usage_requirements() { ((Model*)0)->~Model(); }
                                          ^
/opt/local/include/boost/concept/detail/general.hpp:39:42: note: in instantiation of member function 'boost::concepts::usage_requirements<boost::range_detail::IncrementableIteratorConcept<boost::numeric::odeint::adaptive_time_iterator ...  requested here
    static void failed() { ((Model*)0)->~Model(); }
                                         ^
/opt/local/include/boost/range/concepts.hpp:136:13: note: in instantiation of member function 'boost::concepts::requirement<boost::concepts::failed ************ ... requested here
            BOOST_CONCEPT_USAGE(IncrementableIteratorConcept)
            ^
/opt/local/include/boost/concept/usage.hpp:29:7: note: expanded from macro 'BOOST_CONCEPT_USAGE'
      BOOST_CONCEPT_ASSERT((boost::concepts::usage_requirements<model>)); \
      ^
/opt/local/include/boost/concept/assert.hpp:43:5: note: expanded from macro 'BOOST_CONCEPT_ASSERT'
    BOOST_CONCEPT_ASSERT_FN(void(*)ModelInParens)
    ^
/opt/local/include/boost/concept/detail/general.hpp:71:51: note: expanded from macro 'BOOST_CONCEPT_ASSERT_FN'
    &::boost::concepts::requirement_<ModelFnPtr>::failed>    \
                                                  ^

From the line iterator_core_access::increment(this->derived()); the backtraces agree.

Issue occurs with multiple compilers

I've checked that I encounter the error with both Apple Clang

$ clang++ --version
Apple LLVM version 10.0.1 (clang-1001.0.46.4)

and the Intel compiler

$ icpc --version
icpc (ICC) 19.0.4.233 20190416
ds283 commented 4 years ago

Probably relevant: https://github.com/boostorg/multiprecision/issues/159

ds283 commented 4 years ago

I eventually had time to look into this. I believe I understand the issue, but having got to the key point it turned out the problem had already been written up at the other odeint-v2 repository belonging to boost.org:

https://github.com/boostorg/odeint/issues/40

To summarise the outcome of the ticket linked above, it is commit https://github.com/boostorg/multiprecision/commit/2f1cb020b08a1fbd0b8ea6ec26ff2928b3c7842a that is to blame. The problem is that the boost::multiprecision::number::value_type member type it introduces resolves to boost::multiprecision::number for non-complex types, setting up an infinite recursion in the odeint-v2 metafunction extract_value_type<>.

I've confirmed that my codes build correctly with the immediately-prior commit https://github.com/boostorg/multiprecision/commit/339085567bcc3911b0c194bf5afc13f60c388831 but fail with https://github.com/boostorg/multiprecision/commit/2f1cb020b08a1fbd0b8ea6ec26ff2928b3c7842a itself. For now, this means the workaround is to revert to commit https://github.com/boostorg/multiprecision/commit/339085567bcc3911b0c194bf5afc13f60c388831 of Boost::Multiprecision (or earlier).

To make progress we probably need some input from the maintainers as to how they wish to modify extract_value_type<> to avoid this infinite loop.