Boost ODEint使用Thrust容器/复数类型时受控步进器编译报错咨询
嘿,我帮你捋清楚这个问题——你遇到的编译错误,本质是Boost ODEint的受控步进器对Thrust容器和thrust::complex的元编程支持不完整,而非你的代码逻辑出错。毕竟简单步进器能正常工作,就说明Thrust的基础适配是没问题的,只是受控步进器需要更精细的类型支持。
为什么会出错?
Boost ODEint的受控步进器(比如make_controlled生成的)依赖内部的元编程工具来推导状态类型的属性,比如容器是否可resize、如何处理复数的实虚部等。而Thrust的host_vector和thrust::complex<double>并没有被ODEint的默认类型特征(traits)完全覆盖,导致受控步进器在做类型检查时找不到所需的适配逻辑,进而编译失败。
而简单步进器的内部逻辑更简单,不需要这么多精细的类型推导,所以能和Thrust正常配合。
两种可行的修复方案
方案一:显式指定Thrust兼容的代数/操作类型(推荐)
Boost ODEint的Thrust外部库提供了thrust_algebra和thrust_operations,我们可以在定义步进器时显式指定这两个类型,强制ODEint使用Thrust适配的代数逻辑,绕开默认的类型推导问题。
修改后的完整代码如下:
#include <iostream> #include <thrust/host_vector.h> #include <thrust/transform.h> #include <thrust/complex.h> #include <boost/numeric/odeint.hpp> #include <boost/numeric/odeint/external/thrust/thrust.hpp> using namespace boost::numeric::odeint; typedef thrust::complex<double> c_type; typedef thrust::host_vector<c_type> state_vec; template<typename data_type, typename complex_type> struct multiply { data_type a; multiply(data_type a) : a(a) {} complex_type operator()(const complex_type& x) { return -a * x; } }; template<typename state_type, typename data_type> struct exp_system { const data_type gamma; exp_system(data_type gamma) : gamma(gamma) {} void operator()(const state_type& x, state_type& dxdt, const data_type) { // 这里改用state_type的value_type,避免硬编码c_type,增强通用性 thrust::transform(x.begin(), x.end(), dxdt.begin(), multiply<data_type, typename state_type::value_type>(gamma)); } }; int main() { state_vec x(2); x[0] = c_type(1.0, 0.0); x[1] = c_type(0.10, 0.0); double gamma = 0.15; double t_final = 10.0; // 关键:显式指定Thrust兼容的代数和操作给Dopri5步进器 runge_kutta_dopri5<state_vec, double, state_vec, double, thrust_algebra, thrust_operations> stepper; auto c_stepper = make_controlled(1.E-5, 1.E-5, stepper); auto n_steps = integrate_adaptive(c_stepper, exp_system<state_vec, double>(gamma), x, 0.0, t_final, 0.01); std::cout << n_steps << std::endl; return 0; }
方案二:手动特化ODEint的类型特征
如果不想修改步进器的定义,可以手动为thrust::host_vector特化ODEint所需的is_resizeable和state_wrapper traits,告诉ODEint如何处理这个容器:
在代码开头添加以下特化代码:
#include <boost/numeric/odeint/util/is_resizeable.hpp> #include <boost/numeric/odeint/util/state_wrapper.hpp> namespace boost { namespace numeric { namespace odeint { // 告诉ODEint:thrust::host_vector是可resize的 template<typename T> struct is_resizeable<thrust::host_vector<T>> { typedef boost::true_type type; const static bool value = true; }; // 告诉ODEint如何resize thrust::host_vector template<typename T> struct state_wrapper<thrust::host_vector<T>> { typedef thrust::host_vector<T> type; static type resize( const type &x, size_t n ) { return type( n ); } }; } } }
然后保持你的exp_system中transform的调用修改(用typename state_type::value_type代替硬编码的c_type),就可以正常编译了。
验证说明
两种方案都能解决问题,其中方案一更简洁,直接利用ODEint提供的Thrust适配工具,避免手动特化的繁琐。你可以根据自己的代码习惯选择。
内容的提问来源于stack exchange,提问作者solver

