如何在 R 中将 C++ ODE 求解器与 Rcpp 一起使用?
How to use C++ ODE solver with Rcpp in R?
为了评估 R 和 C++ 求解 ODE 的速度差异,我在 R 中构建了以下 ODE 系统:
modelsir_cpp =function(t,x){
S = x[1]
I1 = x[2]
I2 = x[3]
N=S+I1+I2
with(as.list(parm), {
dS=B*I1-mu*S-beta*(S*(I1+I2)/N)
dI1=beta*(S*(I1+I2)/N)-B*I1-lambda12*I1
dI2=lambda12*I1
res=c(dS,dI1,dI2)
return(res)
})
}
为了解决这个问题,我使用了 deSolve 包。
times = seq(0, 10, by = 1/52)
parm=c(B=0.01,mu=0.008,beta=10,lambda12=1)
xstart=c(S=900,I1=100,I2=0)
out = as.data.frame(lsoda(xstart, times, modelsir, parm))
这行得通。我试图通过在 R 中使用 Rcpp 库,用 C++ 求解器解决同一个系统。这是我添加的内容:
#include <Rcpp.h>
#include <boost/array.hpp>
// include Boost's odeint
#include <boost/numeric/odeint.hpp>
// [[Rcpp::depends(BH)]]
using namespace Rcpp;
using namespace std;
using namespace boost::numeric::odeint;
typedef boost::array< double ,3 > state_type;
// [[Rcpp::export]]
Rcpp::NumericVector my_fun2(const Rcpp::NumericVector &x, const double t){
Function f("modelsir_cpp");
return f(_["t"]=t,_["x"]=x);
}
void eqsir(const state_type &x, state_type &dxdt, const double t){
Rcpp::NumericVector nvec=boost_array_to_nvec(x);
Rcpp::NumericVector nvec2(3);
nvec2=my_fun2(nvec,t);
dxdt=nvec_to_boost_array(nvec2);
}
void write_cout_2( const state_type &x , const double t ) {
// use Rcpp's stream
Rcpp::Rcout << t << '\t' << x[0] << '\t' << x[1] << '\t' << x[2] << endl;
}
typedef runge_kutta_dopri5< state_type > stepper_type;
// [[Rcpp::export]]
bool my_fun10_solver() {
state_type x = { 900 , 100, 0 }; // initial conditions
integrate_adaptive(make_controlled( 1E-12 , 1E-12 , stepper_type () ) ,
eqsir , x , 1.0 , 2 , 1/52 , write_cout_2 );
return true;
}
但是出现错误信息:
In function 'bool my_fun10_solver()':
ex3.cpp:114:64: error: no matching function for call to 'integrate_adaptive(boost::numeric::odeint::result_of::make_controlled > >::type, void (&)(const state_type&, state_type&, double), state_type&, double, int, int, void (&)(const state_type&, double))'
eqsir , x , 1.0 , 2 , 1/52 , write_cout_2 );
我的代码有什么问题?
下面是我根据我的问题改编的脚本。这个脚本运行良好。
#include <Rcpp.h>
#include <boost/array.hpp>
// include Boost's odeint
#include <boost/numeric/odeint.hpp>
// [[Rcpp::depends(BH)]]
using namespace Rcpp;
using namespace std;
using namespace boost::numeric::odeint;
typedef boost::array< double ,3 > state_type;
void rhs( const state_type &x , state_type &dxdt , const double t ) {
dxdt[0] = 3.0/(2.0*t*t) + x[0]/(2.0*t);
dxdt[1] = 3.0/(2.0*t*t) + x[1]/(2.0*t);
dxdt[2] = 3.0/(2.0*t*t) + x[1]/(2.0*t);
}
void write_cout( const state_type &x , const double t ) {
// use Rcpp's stream
Rcpp::Rcout << t << '\t' << x[0] << '\t' << x[1] << '\t' << x[2] << endl;
}
typedef runge_kutta_dopri5< state_type > stepper_type;
// [[Rcpp::export]]
bool boostExample() {
state_type x = { 1.0 , 1.0, 1.0 }; // initial conditions
integrate_adaptive(make_controlled( 1E-12 , 1E-12 , stepper_type () ) ,
rhs , x , 1.0 , 10.0 , 0.1 , write_cout );
return true;
}
你能试试下面的方法吗?
integrate_adaptive(make_controlled( 1E-12 , 1E-12 , stepper_type () ) ,
eqsir , x , 1.0 , 2.0 , 1.0/52.0 , write_cout_2 );
虽然有一些优化建议。你正在解决一个 ODE,这表明你是物理学家或工程师,这太棒了,我也是物理学家,物理学家只是摇滚。
但计算机科学家也是如此,他们尽最大努力尽可能快地做事。他们构建编译器,这消除了很多相关的思考。尽力帮助他们。
我为什么要告诉你这个?回到 ODE 的话题。 boost 的自适应积分器可能调用 eqsir()
1e9
次。尝试使该功能尽可能高效。考虑重写 my_fun2
以便 x
被覆盖而不是创建一个新的 return 它。 x
是最后一个状态。除非你想绘制动态图,否则你不会关心它。
void my_fun2(Rcpp::NumericVector &x, const double t);
还有
Rcpp::NumericVector nvec=boost_array_to_nvec(x);
Rcpp::NumericVector nvec2(3);
必须在每次调用时分配新内存。最后,考虑将 nvec
和 state_type
的 2 个转换器与我作为第二个选项编写的参考文献一起使用。您的新 eqsir
看起来像这样 运行 可能会快很多。
Rcpp::NumericVector nvec(3); // declared outside
void eqsir(const state_type &x, state_type &dxdt, const double t){
boost_array_to_nvec(x, nvec);
my_fun2(nvec,t);
nvec_to_boost_array(nvec, dxdt);
}
为了评估 R 和 C++ 求解 ODE 的速度差异,我在 R 中构建了以下 ODE 系统:
modelsir_cpp =function(t,x){
S = x[1]
I1 = x[2]
I2 = x[3]
N=S+I1+I2
with(as.list(parm), {
dS=B*I1-mu*S-beta*(S*(I1+I2)/N)
dI1=beta*(S*(I1+I2)/N)-B*I1-lambda12*I1
dI2=lambda12*I1
res=c(dS,dI1,dI2)
return(res)
})
}
为了解决这个问题,我使用了 deSolve 包。
times = seq(0, 10, by = 1/52)
parm=c(B=0.01,mu=0.008,beta=10,lambda12=1)
xstart=c(S=900,I1=100,I2=0)
out = as.data.frame(lsoda(xstart, times, modelsir, parm))
这行得通。我试图通过在 R 中使用 Rcpp 库,用 C++ 求解器解决同一个系统。这是我添加的内容:
#include <Rcpp.h>
#include <boost/array.hpp>
// include Boost's odeint
#include <boost/numeric/odeint.hpp>
// [[Rcpp::depends(BH)]]
using namespace Rcpp;
using namespace std;
using namespace boost::numeric::odeint;
typedef boost::array< double ,3 > state_type;
// [[Rcpp::export]]
Rcpp::NumericVector my_fun2(const Rcpp::NumericVector &x, const double t){
Function f("modelsir_cpp");
return f(_["t"]=t,_["x"]=x);
}
void eqsir(const state_type &x, state_type &dxdt, const double t){
Rcpp::NumericVector nvec=boost_array_to_nvec(x);
Rcpp::NumericVector nvec2(3);
nvec2=my_fun2(nvec,t);
dxdt=nvec_to_boost_array(nvec2);
}
void write_cout_2( const state_type &x , const double t ) {
// use Rcpp's stream
Rcpp::Rcout << t << '\t' << x[0] << '\t' << x[1] << '\t' << x[2] << endl;
}
typedef runge_kutta_dopri5< state_type > stepper_type;
// [[Rcpp::export]]
bool my_fun10_solver() {
state_type x = { 900 , 100, 0 }; // initial conditions
integrate_adaptive(make_controlled( 1E-12 , 1E-12 , stepper_type () ) ,
eqsir , x , 1.0 , 2 , 1/52 , write_cout_2 );
return true;
}
但是出现错误信息:
In function 'bool my_fun10_solver()': ex3.cpp:114:64: error: no matching function for call to 'integrate_adaptive(boost::numeric::odeint::result_of::make_controlled > >::type, void (&)(const state_type&, state_type&, double), state_type&, double, int, int, void (&)(const state_type&, double))' eqsir , x , 1.0 , 2 , 1/52 , write_cout_2 );
我的代码有什么问题?
下面是我根据我的问题改编的脚本。这个脚本运行良好。
#include <Rcpp.h>
#include <boost/array.hpp>
// include Boost's odeint
#include <boost/numeric/odeint.hpp>
// [[Rcpp::depends(BH)]]
using namespace Rcpp;
using namespace std;
using namespace boost::numeric::odeint;
typedef boost::array< double ,3 > state_type;
void rhs( const state_type &x , state_type &dxdt , const double t ) {
dxdt[0] = 3.0/(2.0*t*t) + x[0]/(2.0*t);
dxdt[1] = 3.0/(2.0*t*t) + x[1]/(2.0*t);
dxdt[2] = 3.0/(2.0*t*t) + x[1]/(2.0*t);
}
void write_cout( const state_type &x , const double t ) {
// use Rcpp's stream
Rcpp::Rcout << t << '\t' << x[0] << '\t' << x[1] << '\t' << x[2] << endl;
}
typedef runge_kutta_dopri5< state_type > stepper_type;
// [[Rcpp::export]]
bool boostExample() {
state_type x = { 1.0 , 1.0, 1.0 }; // initial conditions
integrate_adaptive(make_controlled( 1E-12 , 1E-12 , stepper_type () ) ,
rhs , x , 1.0 , 10.0 , 0.1 , write_cout );
return true;
}
你能试试下面的方法吗?
integrate_adaptive(make_controlled( 1E-12 , 1E-12 , stepper_type () ) ,
eqsir , x , 1.0 , 2.0 , 1.0/52.0 , write_cout_2 );
虽然有一些优化建议。你正在解决一个 ODE,这表明你是物理学家或工程师,这太棒了,我也是物理学家,物理学家只是摇滚。
但计算机科学家也是如此,他们尽最大努力尽可能快地做事。他们构建编译器,这消除了很多相关的思考。尽力帮助他们。
我为什么要告诉你这个?回到 ODE 的话题。 boost 的自适应积分器可能调用 eqsir()
1e9
次。尝试使该功能尽可能高效。考虑重写 my_fun2
以便 x
被覆盖而不是创建一个新的 return 它。 x
是最后一个状态。除非你想绘制动态图,否则你不会关心它。
void my_fun2(Rcpp::NumericVector &x, const double t);
还有
Rcpp::NumericVector nvec=boost_array_to_nvec(x);
Rcpp::NumericVector nvec2(3);
必须在每次调用时分配新内存。最后,考虑将 nvec
和 state_type
的 2 个转换器与我作为第二个选项编写的参考文献一起使用。您的新 eqsir
看起来像这样 运行 可能会快很多。
Rcpp::NumericVector nvec(3); // declared outside
void eqsir(const state_type &x, state_type &dxdt, const double t){
boost_array_to_nvec(x, nvec);
my_fun2(nvec,t);
nvec_to_boost_array(nvec, dxdt);
}