How would I whip up a custom stepper for the ODE integrator in boost? I know how to do that for an array whose size is known at compile time. A simple implementation is like this
#include <boost/array.hpp>
#include <boost/numeric/odeint.hpp>
using namespace std;
using namespace boost::numeric::odeint;
typedef boost::array<double, 2> state_type;
template <size_t N>
class euler_stepper {
public:
typedef double value_type;
typedef double time_type;
typedef unsigned short order_type;
typedef boost::numeric::odeint::stepper_tag stepper_category;
static order_type order(void) { return(1); }
template<class System>
void do_step(System system, state_type &x, time_type t, time_type dt) const {
state_type der;
system(x , der);
for(size_t i=0 ; i<x.size(); i++) {
x[i] += dt*der[i];
}
}
};
struct rhs {
void operator()(const state_type &x, state_type &dxdt) const {
for(int i=0; i < x.size(); i++) dxdt[i] = -x[i];
}
};
struct observer {
void operator()(const state_type &x , double t) const {
for(int i=0; i < x.size(); i++) cout << x[i] << '\t';
cout << endl;
}
};
int main(int argc , char **argv) {
double dt = 0.01;
state_type x = {{2.0, 1.0}};
integrate_const(euler_stepper<2>(), rhs(), x, 0.0, 0.1, dt, observer());
return 0;
}
But how should I change the implementation, if I wanted to work with a state_type like this
typedef vector<complex<double>> state_type;
Basically, I would like to pass the size of the state vector as an argument to main.
Thanks,
Zoltán
Try this:
template < typename State >
class euler_stepper {
public:
using state_type = State;
using value_type = typename state_type::value_type;
using time_type = value_type;
using order_type = unsigned short;
using stepper_category = boost::numeric::odeint::stepper_tag;
static order_type order(void) { return(1); }
template<class System>
void do_step( System system , state_type &x , time_type t , time_type dt ) const
{
state_type der;
system(x , der);
for(size_t i=0 ; i<x.size(); i++) {
x[i] += dt*der[i];
}
}
};
You can also fix the state type for your solver, for example:
class euler_stepper
{
public:
using state_type = vector< complex< double > >;
using value_type = double;
using time_type = double;
// ...
};
Related
I ran the following code which solves a simple differential equation. The result seems to depend on tsteps. The result I get for tsteps = 100 is 9.688438503116524e-15, but for tsteps = 1000 the answer is 7.124585369895499e-218 much closer to the expected result.
#include <iostream>
#include <iomanip>
#include <boost/numeric/odeint.hpp> // odeint function definitions
using namespace std;
using namespace boost::numeric::odeint;
typedef std::vector< double> state_type;
int tsize = 1;
void my_observer( const state_type &x, const double t );
void initarrays(state_type &x)
{
x[0]=1.0e0;
}
void my_system( const state_type &x , state_type &dxdt , const double t )
{
dxdt[0]=-x[0];
}
void my_observer( const state_type &x, const double t )
{
std::cout<<t<<" ";
for(int i=0;i<tsize;i++)
{
std::cout<<x[i]<<" ";
}
std::cout<<std::endl;
}
int main()
{
std::cout.setf ( std::ios::scientific, std::ios::floatfield );
std::cout.precision(15);
int size=tsize;
state_type x0(size);
double err_abs = 1.0e-12;
double err_rel = 1.0e-12;
double a_x = 1.0;
double a_dxdt = 1.0;
initarrays(x0);
double t0 = 0.0e0;
int tsteps = 1000;
double t1 = 500.0e0;
double dt = (t1-t0)/((double)tsteps);
typedef runge_kutta_fehlberg78< state_type > solver;
typedef controlled_runge_kutta< solver > controller;
my_observer(x0,t0);
for(int ts=0;ts<tsteps;ts++)
{
integrate_adaptive( make_controlled( err_abs , err_rel , solver() ), my_system, x0 , t0+ts*dt , t0+(1+ts)*dt , dt);
my_observer(x0,t0+(1+ts)*dt);
}
}
In my C++ script (run in R using Rcpp), I defined :
typedef boost::array< double ,3 > state_type;
Now, I want to create a function to transform a state_type variable to a Rcpp::NumericVector variable, and another function that do the inverse. How can do that?
I need to do that in order to use R function into C++.
If you want to have seamless integration you will have to extend Rcpp::as<T>(obj) and Rcpp::wrap(obj). How to do this is covered in the vignette Extending Rcpp. However, there is also an excellent entry in the Rcpp gallery that already covers a very similar case: Converting to and from boost::numeric::ublas::vector<double>.
If you are content with only using state_type and not the general template, then you can use something like this:
#include <RcppCommon.h>
// [[Rcpp::depends(BH)]]
#include <boost/array.hpp>
typedef boost::array< double ,3 > state_type;
namespace Rcpp {
// non-intrusive extension via template specialisation
template <> state_type as(SEXP s);
// non-intrusive extension via template specialisation
template <> SEXP wrap(const state_type &s);
}
#include <Rcpp.h>
// define template specialisations for as and wrap
namespace Rcpp {
template <> state_type as(SEXP stsexp) {
Rcpp::NumericVector st(stsexp);
state_type result;
if (st.size() != result.size()) Rcpp::stop("Incompatible length!");
std::copy(st.begin(), st.end(), result.begin());
return result;
}
template <> SEXP wrap(const state_type &st) {
Rcpp::NumericVector result(st.size());
std::copy(st.begin(), st.end(), result.begin());
return Rcpp::wrap(result);
}
}
// make use of the new possibilities
// [[Rcpp::export]]
state_type alterStateType(state_type &st) {
for (size_t i = 0; i < st.size(); ++i) {
st[i] = i * st[i];
}
return st;
}
/*** R
alterStateType(1:3)
*/
How about
Rcpp::NumericVector boost_array_to_nvec(state_type const& s) {
Rcpp::NumericVector nvec(s.size());
for (size_t i = 0; i < s.size(); ++i) {
nvec[i] = s[i];
}
return nvec;
}
state_type nvec_to_boost_array(Rcpp::NumericVector const& nvec) {
state_type s;
for (size_t i = 0; i < s.size(); ++i) {
s[i] = nvec[i];
}
return s;
}
...
state_type s {0,0,0};
Rcpp::NumericVector nvec = boost_array_to_nvec(s);
s = nvec_to_boost_array(nvec);
If you have to do this a lot of times. This might not be the most efficient way of doing the transformation. But now you have to look that nvec is already allocated to the correct size.
void boost_array_to_nvec(state_type const& s, Rcpp::NumericVector& nvec) {
for (size_t i = 0; i < s.size(); ++i) {
nvec[i] = s[i];
}
}
void nvec_to_boost_array (Rcpp::NumericVector const& nvec, state_type& s) {
for (size_t i = 0; i < s.size(); ++i) {
s[i] = nvec[i];
}
}
...
state_type s {0,0,0};
Rcpp::NumericVector nv(3);
boost_array_to_nvec(s, nv);
nvec_to_boost_array(nv, s);
I'm trying to use solve my ode system and use openmp. When I try stepper.do_step with:
typedef runge_kutta_dopri5< state_type , double , state_type , double , openmp_range_algebra > stepper;
I define my state_type as std::vector
And I compile I get:
error: expected unqualified-id before '.' token stepper.do_step(c , Y , t, dt );
Is it a problem of my typedef or I can't use do_step with openmp?
#include <boost/numeric/odeint.hpp>
#include<omp.h>
#include <boost/numeric/odeint/external/openmp/openmp.hpp>
typedef std::vector< double > state_type;
struct T
{
double m_param1;
double m_param2;
};
class sys
{
struct T T1;
public:
sys(struct T G) : T1(G) {}
void operator() ( state_type const& Y , state_type& dY , double t )
{
const size_t N = Y.size();
#pragma omp parallel for schedule(runtime)
for (size_t aux = 0; aux <= N; aux++) {
mdlfnt(Y,dY,t,T1.m_currents,T1.m_stim); //Here is my ode system
}
}
};
struct T T2;
state_type Y(45); // initial conditions
void main()
{
typedef runge_kutta_dopri5< state_type , double , state_type , double , openmp_range_algebra , default_operations , never_resizer > stepper;
int number_threads = 1;
omp_set_num_threads(number_threads);
int chunk_size = omp_get_max_threads();
omp_set_schedule( omp_sched_static , chunk_size );
for(int i=0;i<nd;i++){
sys c(T2);
stepperdo_step(c , Y , t, dt );
}
}
I am continually writing something akin to
std::vector< std::vector< double > > A(N, std::vector< double >(M));
and I would like to replace this with something like
matrix A(N,M);
by using a #define directive. I've looked at #define directives and think I can create a function like matrix(A,N,M) that would declare a vector of vectors as follows:
#define matrix(A, N, M) std::vector< std::vector< double > > A(N, std::vector< double >(M))
but I would rather not declare my matrices as matrix(A,N,M), but rather matrix A(N,M). My question is - how do I use the #define directives to account for changing a variable name?
You can use typedef and define type, something like that:
#include <vector>
using namespace std;
int main()
{
int N = 10;
typedef std::vector< std::vector<double> matrix;
matrix A(N, std::vector< double >(N));
return 0;
}
or more safety (if you don't know, that matrix will be right)
int main()
{
int N = 10;
typedef std::vector< std::array<double, 5> > matrix;
matrix A(N, std::array< double , 5 >());
return 0;
}
my wrapper for matrix with vectors
#include <iostream>
#include <vector>
#include <exception>
#include <algorithm>
template< typename T >
class WrapperMatrix
{
public:
WrapperMatrix(const int& weight, const int& length);
void pushLine(const std::vector<T>&&);
void pushColumn(const std::vector<T>&&);
void display();
private:
std::vector<std::vector<T>> matrix;
};
template<typename T>
WrapperMatrix<T>::WrapperMatrix(const int& weight, const int& length)
{
this->matrix = std::vector<std::vector<T>>(weight, std::vector<T>(length));
}
template <typename T>
void WrapperMatrix<T>::pushLine(const std::vector<T>&& newLine)
{
if (newLine.size() == this->matrix.at(0).size())
matrix.emplace_back(std::move(newLine));
else
throw std::invalid_argument("Invalis syntax");
}
template <typename T>
void WrapperMatrix<T>::pushColumn(const std::vector<T>&& newColumn)
{
if (newColumn.size() == this->matrix.size())
{
for (int i = 0; i < matrix.size(); ++i)
matrix.at(i).emplace_back(std::move(newColumn.at(i)));
}
else
throw std::invalid_argument("Invalid syntax");
}
template<typename T>
void WrapperMatrix<T>::display()
{
for (int i = 0; i < matrix.size(); ++i)
{
for (int j = 0; j < matrix.at(0).size(); ++j)
std::cout << matrix.at(i).at(j);
std::cout << std::endl;
}
}
int main()
{
std::vector<int> v1{ 1,2,3,4,5 };
std::vector<int> v2{ 1,2,3,4,5,6 };
std::vector<int> v3{ 2,3,4,5,6 };
WrapperMatrix<int> vw(5,5);
try {
vw.pushLine(std::move(v1));
vw.pushColumn(std::move(v2));
//vw.pushLine(std::move(v3));
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
vw.display();
return 0;
}
Alternative answer to typedef
using matrix = std::vector< std::vector<double>>;
This form can be more readable, especially with function and array types. E.g. using arr10 = Foo[10] is clearer than typedef Foo arra10[10]. The = sign clearly separates what's being defined and how it's defined.
(Ignoring the whole "matrix is not a vector of vectors" discussion)
As I know function call operator overloading is used to pass arbitrary number of parameter to a function inside a class. Is it also possible to pass a 2-Dimensional array ?
something like this in the following code:Edited
class harm_osc {
vector< vector<double> >& m_gam;
public:
harm_osc( vector< vector<double> > &gam ) : m_gam(gam) { }
void operator() ( const state_type &x , state_type &dxdt , const double /* t */ )
{
dxdt[0] = m_gam[0][0]*x[1];
dxdt[1] = m_gam[1][0]*x[0] - m_gam[1][1]*x[1];
}
};
When we have a large system of equations there are a lot of parameters.
Here an example from odeint library with some modification, with a single parameter:Edited
#include <iostream>
#include <vector>
#include <boost/numeric/odeint.hpp>
using namespace std;
/* The type of container used to hold the state vector */
typedef vector< double > state_type;
/* The rhs of x' = f(x) defined as a class */
class harm_osc {
vector< vector<double> >& m_gam;
public:
harm_osc( vector< vector<double> > &gam ) : m_gam(gam) { }
void operator() ( const state_type &x , state_type &dxdt , const double /* t */ )
{
dxdt[0] = m_gam[0][0]*x[1];
dxdt[1] = m_gam[1][0]*x[0] - m_gam[2][2]*x[1];
}
};
int main(int /* argc */ , char** /* argv */ )
{
using namespace boost::numeric::odeint;
//[ state_initialization
state_type x(2);
x[0] = 1.0; // start at x=1.0, p=0.0
x[1] = 0.0;
//]
//[ integration_class
vector< vector<double> > par {{1.,0.},{-1.,0.15}};
harm_osc ho(par);
size_t steps = integrate( ho ,
x , 0.0 , 10.0 , 0.1);
//]
}
Thanks for any guide or comment.