Discussion:
[Rcpp-devel] Expose cpp class with built-in boost odeint to R
Simon Woodward
2018-09-14 03:17:12 UTC
Permalink
I am developing a simulation model in C++ and I want to expose it to R for testing. The model is in a C++ class and uses the boost::odeint library for numerical integration. I am also using an unordered_map to expose the model variables to the user.
I am having some trouble exposing the class functionality to C++, either as separate // [[Rcpp::export]] methods or using RCPP_MODULE. The following code successfully instantiates the model and returns the unordered_map but crashes Rcpp if I try to access the advance(t,dt) method.

# sample code embedded in an R script
library(Rcpp)

Sys.setenv("PKG_CXXFLAGS"='-I"C:/boost/boost_1_66_0"')

sourceCpp(code='
#include <unordered_map>
#include <string>
#define BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE
#include <boost/numeric/odeint.hpp>
#include <Rcpp.h>

// Enable C++11 via this plugin (Rcpp 0.10.3 or later)
// [[Rcpp::plugins(cpp11)]]

using namespace std;
using namespace boost::numeric::odeint;
using namespace Rcpp;

class spring{

public:

// unordered_map gives user efficient access to variables by name
// https://stackoverflow.com/questions/8483985/obtaining-list-of-keys-and-values-from-unordered-map
// will be more efficient if we intelligently reserve storage for the map at construction time
// https://stackoverflow.com/questions/15135900/how-to-pass-a-mapdouble-t-from-c-to-r-with-rcpp
unordered_map< string , double > variable;

private:

// declare state_type and system_time
typedef boost::array< double , 3 > state_type;
double system_time;
const int system_variables = 12; // number of variables available to user

// declare boost::odeint stepper
typedef runge_kutta4< state_type > stepper_type;
stepper_type stepper;

// state variables
double time;
double xd;
double x;

// constants
const double k = 0.12;
const double a = 1.0;
const double w = 1.0;
const double g = 9.81;
const double mass = 0.03;
const double xdic = 0.0;
const double xic = 0.0;

// other variables
double xdd = -999;

public:

// constructor
spring(){

// reserve buckets to minimise storage and avoid rehashing
// note: only need variables the user needs to set or get
variable.reserve( system_variables );

}

void initialise_model( double a_system_time ){

// initialise system time
system_time = a_system_time;

// initial calculations

// initial conditions
time = 0.0;
xd = xdic;
x = xic;

}

void pull_variables_from_model(){

// generic system property
variable["system_time"] = system_time;

// state variables
variable["time"] = time;
variable["xd"] = xd;
variable["x"] = x;

// constants
variable["k"] = k;
variable["a"] = a;
variable["w"] = w;
variable["g"] = g;
variable["mass"] = mass;
variable["xdic"] = xdic;
variable["xic"] = xic;

// other variables
variable["xdd"] = xdd;

}

void push_variables_to_model(){

// generic system property
system_time = variable["system_time"];

// state variables
time = variable["time"];
xd = variable["xd"];
x = variable["x"];

// constants (cant change these)
// k = variable["k"];
// a = variable["a"];
// w = variable["w"];
// g = variable["g"];
// mass = variable["mass"];
// xdic = variable["xdic"];
// xic = variable["xic"];

// other variables
xdd = variable["xdd"];

}

private:

state_type get_state(){

state_type a_state;

// return current state
a_state[0] = time;
a_state[1] = xd;
a_state[2] = x;

return( a_state );

}

void set_state( state_type a_state ){

// set state
time = a_state[0];
xd = a_state[1];
x = a_state[2];

}

public:

void calculate_rate(){

// calculations
xdd = ( mass * g - k * xd - a * x ) / mass;

}

public:

// called by boost::odeint::integrate()
void operator()( const state_type &a_state , state_type &a_rate, double a_time ){

// set state
system_time = a_time;
time = a_state[0];
xd = a_state[1];
x = a_state[2];

// calculate rate
calculate_rate();

// return rate
a_rate[0] = 1.0;
a_rate[1] = xdd;
a_rate[2] = xd;

}

void advance( const double end_time , const double time_step ){

double a_time;
state_type a_state;

a_time = system_time;
a_state = get_state();

// https://stackoverflow.com/questions/10976078/using-boostnumericodeint-inside-the-class
integrate_const( stepper , *this , a_state, a_time , end_time , time_step );

system_time = end_time;
set_state( a_state );
calculate_rate();

}

}; // end class spring_type

spring my_spring;

// https://stackoverflow.com/questions/34181135/converting-an-stdmap-to-an-rcpplist

// [[Rcpp::export]]
void initialise_model( double t ){
my_spring.initialise_model( t );
}

// [[Rcpp::export]]
void pull_variables_from_model(){
my_spring.pull_variables_from_model();
}

// [[Rcpp::export]]
NumericVector get_spring_variables(){
NumericVector my_vector;
my_vector = my_spring.variable; // coerces unordered_map to named vector
return my_vector;
}

// whenever I try to expose the advance method I get an error
// // [[Rcpp::export]]
// void advance( double t , double dt ){
// my_spring.advance( t , dt );
// }

int main(){
// do nothing
}
') # end of sourceCpp

# instantiate and query the model
initialise_model(0)
pull_variables_from_model()
x <- get_spring_variables()
print(x) # this all works.

# I now want to do this
advance( 1 , 0.1)
pull_variables_from_model()
x <- get_spring_variables() # is this necessary?
print(x)

# end of R script

Thank you

Simon Woodward
Senior Scientist (Mathematical Modelling)
DairyNZ
Cnr Ruakura & Morrinsville Roads | Newstead | Private Bag 3221| Hamilton 3240 | NEW ZEALAND
Ph +64 7 858 3750 | DDI +64 7 858 3787 | Fax +64 7 858 3751
Web www.dairynz.co.nz<http://www.dairynz.co.nz/> | www.GoDairy.co.nz<http://www.godairy.co.nz/> | www.getfresh.co.nz<http://www.getfresh.co.nz/>
Ralf Stubner
2018-09-14 08:05:06 UTC
Permalink
Post by Simon Woodward
I am developing a simulation model in C++ and I want to expose it to R for testing. The model is in a C++ class and uses the boost::odeint library for numerical integration. I am also using an unordered_map to expose the model variables to the user.
I am having some trouble exposing the class functionality to C++, either as separate // [[Rcpp::export]] methods or using RCPP_MODULE. The following code successfully instantiates the model and returns the unordered_map but crashes Rcpp if I try to access the advance(t,dt) method.
What do you mean with "crashes Rcpp"? On my system it mearly gives a
compiler error:

/usr/include/c++/6/bits/stl_iterator_base_funcs.h: In instantiation of
‘void std::advance(_InputIterator&, _Distance) [with _InputIterator =
Post by Simon Woodward
# sample code embedded in an R script
With the given ratio of C++ to R code it would have been better to use a
C++ file with R code embedded via /*** R ... */.
Post by Simon Woodward
library(Rcpp)
Sys.setenv("PKG_CXXFLAGS"='-I"C:/boost/boost_1_66_0"')
The BH package can help with that:

// [[Rcpp::depends(BH)]]
Post by Simon Woodward
sourceCpp(code='
#include <unordered_map>
#include <string>
#define BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE
#include <boost/numeric/odeint.hpp>
#include <Rcpp.h>
// Enable C++11 via this plugin (Rcpp 0.10.3 or later)
// [[Rcpp::plugins(cpp11)]]
using namespace std;
The above quoted error message indicates that your advance method and
std::advance are getting mixed up. Remove this "using namespace std;"
and handle to resulting errors by using "std::" or "using std::...;".

BTW, for larger pieces of code I would also get rid of "using namespace
Rcpp;".

cheerio
ralf
--
Ralf Stubner
Senior Software Engineer / Trainer

daqana GmbH
Dortustraße 48
14467 Potsdam

T: +49 331 23 61 93 11
F: +49 331 23 61 93 90
M: +49 162 20 91 196
Mail: ***@daqana.com

Sitz: Potsdam
Register: AG Potsdam HRB 27966 P
Ust.-IdNr.: DE300072622
Geschäftsführer: Prof. Dr. Dr. Karl-Kuno Kunze
Dirk Eddelbuettel
2018-09-14 13:36:37 UTC
Permalink
Simon,

"What Ralf said" -- it's all spot on. Get to something smaller, shorter,
simpler and it should sort itself out in due course.

Dirk
--
http://dirk.eddelbuettel.com | @eddelbuettel | ***@debian.org
Simon Woodward
2018-09-14 22:05:42 UTC
Permalink
Hi Ralf

Thank you so much, your advice was extremely helpful! It works now.
My simulation model will be 1000+ lines of C++, this is a prototype, I embedded the code inline to make it easier to post as a reprex to this forum.

Follow up questions:
- do I need to worry about destructing my global variable my_spring (the instance of my class)? If so, how do I do this?
- for read/write efficiency, can/should I make a persistent link (Rccp::XPtr?) between the my_spring.variable property and an object or function in R? If so, how do I do this? Or is it better simply to copy-pass the data when needed and let Rccp handle the conversion from std::unordered_map<string,double> to an R named vector?

// https://www.r-bloggers.com/external-pointers-with-rcpp/

Thanks!

Simon


-----Original Message-----
From: Rcpp-devel [mailto:rcpp-devel-***@lists.r-forge.r-project.org] On Behalf Of Ralf Stubner
Sent: Friday, 14 September 2018 8:05 PM
To: rcpp-***@lists.r-forge.r-project.org
Subject: Re: [Rcpp-devel] Expose cpp class with built-in boost odeint to R
Post by Simon Woodward
I am developing a simulation model in C++ and I want to expose it to R for testing. The model is in a C++ class and uses the boost::odeint library for numerical integration. I am also using an unordered_map to expose the model variables to the user.
I am having some trouble exposing the class functionality to C++, either as separate // [[Rcpp::export]] methods or using RCPP_MODULE. The following code successfully instantiates the model and returns the unordered_map but crashes Rcpp if I try to access the advance(t,dt) method.
# sample code embedded in an R script
With the given ratio of C++ to R code it would have been better to use a
C++ file with R code embedded via /*** R ... */.
Post by Simon Woodward
library(Rcpp)
Sys.setenv("PKG_CXXFLAGS"='-I"C:/boost/boost_1_66_0"')
The BH package can help with that:

// [[Rcpp::depends(BH)]]
Post by Simon Woodward
sourceCpp(code='
#include <unordered_map>
#include <string>
#define BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE
#include <boost/numeric/odeint.hpp>
#include <Rcpp.h>
// Enable C++11 via this plugin (Rcpp 0.10.3 or later)
// [[Rcpp::plugins(cpp11)]]
using namespace std;
The above quoted error message indicates that your advance method and std::advance are getting mixed up. Remove this "using namespace std;"
and handle to resulting errors by using "std::" or "using std::...;".

BTW, for larger pieces of code I would also get rid of "using namespace Rcpp;".

cheerio
ralf

--
Ralf Stubner
Senior Software Engineer / Trainer

daqana GmbH
Dortustraße 48
14467 Potsdam

T: +49 331 23 61 93 11
F: +49 331 23 61 93 90
M: +49 162 20 91 196
Mail: ***@daqana.com

Sitz: Potsdam
Register: AG Potsdam HRB 27966 P
Ust.-IdNr.: DE300072622
Geschäftsführer: Prof. Dr. Dr. Karl-Kuno Kunze
Ralf Stubner
2018-09-17 11:00:59 UTC
Permalink
Post by Simon Woodward
My simulation model will be 1000+ lines of C++, this is a prototype, I embedded the code inline to make it easier to post as a reprex to this forum.
A C++ file with a special /*** R ... */ comment would still be a minimal
example. Just look at the code RStudio produces by default when you
create a new C++ file. When you use Rcpp::sourceCpp() on that file, not
only the code is compiled and linked, but the R code within this special
comment is executed. RStudio does that by default when you source the
file, e.g. via C-S-Return.
Post by Simon Woodward
- do I need to worry about destructing my global variable my_spring (the instance of my class)? If so, how do I do this?
Global variables get destructed when the program ends. I would get rid
the global variable, making it local to the translation unit. One way to
achieve that would be an anonymous namespace.
Post by Simon Woodward
- for read/write efficiency, can/should I make a persistent link (Rccp::XPtr?) between the my_spring.variable property and an object or function in R? If so, how do I do this? Or is it better simply to copy-pass the data when needed and let Rccp handle the conversion from std::unordered_map<string,double> to an R named vector?
I am not sure what you are trying to do here. But as a general rule: try
to write correct and working code first and do any performance
optimizations when you know where the bottlenecks are.

cheerio
ralf
--
Ralf Stubner
Senior Software Engineer / Trainer

daqana GmbH
Dortustraße 48
14467 Potsdam

T: +49 331 23 61 93 11
F: +49 331 23 61 93 90
M: +49 162 20 91 196
Mail: ***@daqana.com

Sitz: Potsdam
Register: AG Potsdam HRB 27966 P
Ust.-IdNr.: DE300072622
Geschäftsführer: Prof. Dr. Dr. Karl-Kuno Kunze
Loading...