ODEint: adaptive integration with arbitrary precision - c++

Is it possible for ODEint to use adaptive integration routines with arbitrary precision arithmetic? For example, I'd like to use the Boost multiprecision libraries with the integrate_adaptive() function with a controlled stepper. The ODEint documentation gives examples for using arbitrary precision arithmetic for integrate_const(), but I can't modify them to use the adaptive integrator.
I've also tried using iterators (e.g. make_adaptive_time_iterator...) but run into similar problems. For concreteness this is a simple code I am looking to get working:
#include <iostream>
//[ mp_lorenz_defs
#include <boost/numeric/odeint.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
using namespace std;
using namespace boost::numeric::odeint;
typedef boost::multiprecision::cpp_dec_float_50 value_type;
//typedef double value_type;
typedef boost::array< value_type , 3 > state_type;
//]
//[ mp_lorenz_rhs
struct lorenz
{
void operator()( const state_type &x , state_type &dxdt , value_type t ) const
{
const value_type sigma( 10 );
const value_type R( 28 );
const value_type b( value_type( 8 ) / value_type( 3 ) );
dxdt[0] = sigma * ( x[1] - x[0] );
dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
dxdt[2] = -b * x[2] + x[0] * x[1];
}
};
//]
int main( int argc , char **argv )
{
//[ mp_lorenz_int
state_type x = {{ value_type( 10.0 ) , value_type( 10.0 ) , value_type( 10.0 ) }};
auto stepper = make_controlled( 1.0e-16 , 1.0e-16 , runge_kutta_cash_karp54< state_type >() );
cout.precision(50);
integrate_adaptive( stepper ,
lorenz() , x , value_type( 0.0 ) , value_type( 0.1 ) , value_type( value_type( 1.0 ) / value_type( 2000.0 ) ) );
//]
cout << x[0] << endl;
return 0;
}
Compiling this returns the error:
Lorenz_mp2.cpp:52:19: error: no matching function for call to 'make_controlled'
auto stepper = make_controlled( value_type(1.0e-16) , value_type(1.0e-16) , runge_kutta_cash_karp54< state_type >() );
If I change the typedef for value_type to double it compiles and runs fine.

Using adaptive integrators with arbitrary precision is possible with odeint. Your code is almost correct, you only forgot to also configure the value_type (used for the internal constants of the stepper, and for the "time" variable t) to be the arbitrary precision type. If you check back in the docs (http://headmyshoulder.github.io/odeint-v2/doc/boost_numeric_odeint/tutorial/using_arbitrary_precision_floating_point_types.html) you'll see that this is done by a second template argument to the stepper. So the correct stepper definition within make_controlled should be:
runge_kutta_cash_karp54< state_type , value_type >()
With this, it should compile and run with arbitrary precision.
Independent of this issue, I would like to add that using conversion from double to a multi_prec type is potentially problematic. For example, 0.1 can not be accurately represented as a double (http://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/). Hence you will end up with a multi_prec value that is not exactly 0.1. I'd advice to always convert from Integers and for example express 0.1 as value_type(1)/value_type(10).

Related

Odeint : system of complex differential equations is possible in c++?

I'm trying to integrate a system of 2 complex valued differential equations in c++ using odeint library.
#include <iostream>
#include <boost/array.hpp>
#include <complex>
#include <boost/numeric/odeint.hpp>
using namespace std;
using namespace boost::numeric::odeint;
const double sigma = 10.0;
const double gamma = 0.1;
const double J12 = 1;
const double G = 1;
const double eta = 0.01;
const double gamma_s = 0.1;
const int n = 2;
typedef boost::array< complex<double> , n > state_type;
//typedef runge_kutta4< complex<double> > stepper_type; tried this and
//typedef runge_kutta_dopri5< complex<double> > stepper_type; // this but still many errors
void lorenz( const state_type &x , state_type &dxdt , double t )
{
dxdt[0] = -0.5*gamma*x[0]
- eta * (abs(x[0])*x[0])//
+ G * conj(x[0])
+ J12 * gamma_s * (x[0]+x[1]);
dxdt[1] = -0.5*gamma*x[1]
- eta * (abs(x[1])*x[1])
+ G * conj(x[1])
+ J12 * gamma_s * (x[0]+x[1]);
}
but I get many errors when compiling. Indeed I get something like :
include/boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp:142:52:
error: no matching function for call to
'boost::numeric::odeint::default_operations::scale_sum4<std::complex<double>, double,
double, double>::scale_sum4(double, std::complex<double>, std::complex<double>,
std::complex<double>)'
142 | typename operations_type::template scale_sum4< value_type ,
time_type , time_type , time_type >( 1.0 , dt*b41 , dt*b42 , dt*b43 ));
I also saw that someone had the same problem but with just one complex valued equation : Do controlled error steppers in boost odeint support complex data types?
Is there a way to fix it also with systems if complex valued ode ?

How to estimate parameter uncertainty using Ceres Solver?

I am using Ceres Solver to perform non-linear curve fits on small data sets. Following the examples I am able to generate perfectly reasonable fit parameters for models that match my data well. I am also trying to compute the parameter variances and this is where things are falling apart. The code executes but results seem incorrect, often many orders of magnitude larger than the the parameter itself. The number of points (x, y) in the data sets I am fitting is similar to the number of parameter in the fit models, e.g. 4 data points, 3 parameters.
I came across a similar SO question here: Ceres: Compute uncertainty on parameter, which was helpful in linking the Ceres wiki on using the covariance class, but the issue was not marked as resolved. I, like the previous poster, looked at the parameter variances produced using the Python lmfit (https://lmfit.github.io/lmfit-py/index.html) package and found that it provides much more reasonable results.
The Ceres description of the covariance class (http://ceres-solver.org/nnls_covariance.html#example-usage) described a potential issue where if the residuals of the cost functor are not scaled correctly, i.e. by the positive semi-definite covariance matrix of the observed data, then the parameter covariance matrix computation can't be trusted. Not being a mathematician, I am not certain how to satisfy this requirement.
Below is some sample code showing the cost function that I've implemented as well as the usage of the covariance class. Any advice would be greatly appreciated.
Cost function:
struct BiExponential1 {
BiExponential1( double x, double y, double s ) : x_( x ), y_( y ), s_( s ) {}
template <typename T>
bool operator()( const T* const a, const T* const b, const T* const c, T* residual ) const {
residual[0] = y_ - a[0] * ( exp( -b[0] * x_ ) - exp( -c[0] * x_ ) ); // observed - estimated = y - ( a' [exp( -b' * x ) - exp(-c' * x)] )
return true;
}
private:
const double x_;
const double y_;
};
Solver/Covariance usage:
double a = init_value_a;
double b = init_value_b;
double c = init_value_c;
double data_x[nData] = {<dummy data>}
double data_y[nData] = {<dummy data>}
for ( int i = 0; i < nData; ++i ) {
problem.AddParameterBlock( &a, 1 );
problem.AddParameterBlock( &b, 1 );
problem.AddParameterBlock( &c, 1 );
problem.AddResidualBlock(
new ceres::AutoDiffCostFunction<BiExponential1, 1, 1, 1, 1>(
new BiExponential1( data_x[i], data_y[i] ) ),
nullptr,
&a,
&b,
&c );
}
// Run the solver and record the results.
ceres::Solve( solverOptions, &problem, &summary );
// Variance estimates
// Code adapted from: http://ceres-solver.org/nnls_covariance.html#example-usage
Covariance::Options covOptions;
// TESTED non-default algorithm type - no effect.
//covOptions.algorithm_type = ceres::CovarianceAlgorithmType::DENSE_SVD;
Covariance covariance( covOptions );
std::vector<std::pair<const double*, const double*> > covariance_blocks;
covariance_blocks.push_back( std::make_pair( &a, &a ) );
covariance_blocks.push_back( std::make_pair( &b, &b ) );
covariance_blocks.push_back( std::make_pair( &c, &c ) );
covariance_blocks.push_back( std::make_pair( &a, &b ) );
covariance_blocks.push_back( std::make_pair( &a, &c ) );
covariance_blocks.push_back( std::make_pair( &b, &c ) );
CHECK( covariance.Compute( covariance_blocks, &problem ) );
// Get the diagonal variance terms
double covariance_aa[1 * 1];
double covariance_bb[1 * 1];
double covariance_cc[1 * 1];
covariance.GetCovarianceBlock( &a, &a, covariance_aa );
covariance.GetCovarianceBlock( &b, &b, covariance_bb );
covariance.GetCovarianceBlock( &c, &c, covariance_cc );

Do controlled error steppers in boost odeint support complex data types?

I encountered a problem with the odeint library using a controlled error stepper together with a complex state type. The code from the example with the complex stuart landau equation, I modified such that it includes an adaptive integrator. The code looks like this now:
#include <iostream>
#include <complex>
#include <boost/array.hpp>
#include <boost/numeric/odeint.hpp>
using namespace std;
using namespace boost::numeric::odeint;
//[ stuart_landau_system_function
typedef complex< double > state_type;
struct stuart_landau
{
double m_eta;
double m_alpha;
stuart_landau( double eta = 1.0 , double alpha = 1.0 )
: m_eta( eta ) , m_alpha( alpha ) { }
void operator()( const state_type &x , state_type &dxdt , double t ) const
{
const complex< double > I( 0.0 , 1.0 );
dxdt = ( 1.0 + m_eta * I ) * x - ( 1.0 + m_alpha * I ) * norm( x ) * x;
}
};
//]
struct streaming_observer
{
std::ostream& m_out;
streaming_observer( std::ostream &out ) : m_out( out ) { }
template< class State >
void operator()( const State &x , double t ) const
{
m_out.precision(10);
m_out << t;
m_out << "\t" << x.real() << "\t" << x.imag() ;
m_out << "\n";
}
};
int main( int argc , char **argv )
{
//[ stuart_landau_integration
state_type x = complex< double >( 1.0 , 0.0 );
bulirsch_stoer< state_type > stepper( 1E-12 , 1E-12 , 1 , 1 );
const double dt = 0.1;
//]
integrate_adaptive( stepper , stuart_landau( 2.0 , 1.0 ) , x , 0.0 , 10.0 , dt , streaming_observer( cout ) );
return 0;
}
However, if I change the definition of the stepper to
bulirsch_stoer< state_type, complex< double > > stepper( 1E-12 , 1E-12 , 1 , 1 );
compilation fails.
My question is: Are complex data types not supported in controlled error steppers? If so, is there a method to circumvent the problem, which arises. Or, is it possible to define an own vector algebra for the complex data type?
The odeint library does support complex data types, including controlled steppers.
As an example, to obtain a controlled stepper in the case of the 4th/5th order Dormand-Prince method you can do this:
runge_kutta_dopri5< state_type > stepper;
auto c_stepper = make_controlled(1.E-12, 1.E-12, stepper);
integrate_adaptive(c_stepper, stuart_landau( 2.0 , 1.0 ) , x , 0.0 , 10.0 , dt , streaming_observer( cout ) );
Where state_type is typedef'd as
typedef complex< double > state_type;
The Bulirsch-Stoer algorithm that is implemented in odeint, however, is already inherently a controlled stepper. It is therefore not possible to apply make_controlled or other strategies to create controlled steppers in this case. There is no such template in the odeint library because it would not make sense.
The first code that you posted already integrates the ODE, with complex values and with a controlled stepper. It is not clear what you are trying to achieve by changing the stepper to
bulirsch_stoer< complex<double>, complex<double> > stepper( 1E-12 , 1E-12 , 1 , 1 );

R interacting with c++ by means of Rcpp to solve large ODE systems

I'm trying to speedup my R code performing some computationally expensive task with C++ and Rcpp. My problem involves approximately a system of 100 equations, so any hint to speed up the computation is welcome.
What I need is to import a matrix MX created in R into a C++ script. The C++ script have to use rows of MX as x0 (x initial values) in a systems of ODE.
To simplify the explanation of my problem, the code below is based on the Lorenz systems.
As it is clear from the quality of my code, I'm new to C++ (and Rcpp).
For clarity, I don't post all my test code that are terrible, bu I really need your help to try solve this problem.
Any help will be really, really appreciated!
Thanks in advance.
#include <boost/array.hpp>
#include <boost/numeric/odeint.hpp>
#include <Rcpp.h>
// [[Rcpp::depends(BH)]]
// [[Rcpp::plugins(cpp11)]]
using namespace std;
using namespace boost::numeric::odeint;
double theta [] = {10.000,28,2.5};
typedef boost::array< double , 3 > state_type;
void lorenz( const state_type &x , state_type &dxdt , double t ) {
dxdt[0] = theta[0] * ( x[1] - x[0] );
dxdt[1] = theta[1] * x[0] - x[1] - x[0] * x[2];
dxdt[2] = -theta[2] * x[2] + x[0] * x[1];
}
struct foo { std::vector<double> a, b, c; };
struct foo f;
//observer should be a function that append a single output row for each input row of mx corresponding to the last integration step.
void append_lorenz(const state_type &x , const double t ) {
f.a.push_back(x[0]);
f.b.push_back(x[1]);
f.c.push_back(x[2]);
}
using namespace Rcpp;
// [[Rcpp::export]]
DataFrame callMain(NumericMatrix mx){
int n = mx.nrow();
NumericMatrix total(mx);
for(int i = 0; i < n; ++i) {
// state_type x should be mx rows
state_type x = total.row(i); // initial conditions
const double dt =0.1;
integrate(lorenz , x , 0.0 , 1.0 , dt , append_lorenz );
}
return DataFrame::create(Named("a") = f.a, Named("b") = f.b, Named("c") = f.c);
}
/*** R
mx=matrix(1:9,3,3)
res <- callMain(mx)
print((res))
*/
the error I get is:
error: conversion from ‘Rcpp::Matrix<14>::Row {aka Rcpp::MatrixRow<14>}’ to non-scalar type ‘state_type {aka boost::array}’ requested
state_type x = total.row(i); // initial conditions
I think the error message is clear enough.
state_type x = total.row(i);
There is no conversion between Rcpp object and boost::array, you need to develop your own.

Comparison of odeint's runge_kutta4 with Matlab's ode45

I would like to use runge_kutta4 method in the odeint C++ library. I've solved the problem in Matlab. My following code in Matlab to solve x'' = -x - g*x', with initial values x1 = 1, x2 = 0, is as follows
main.m
clear all
clc
t = 0:0.1:10;
x0 = [1; 0];
[t, x] = ode45('ODESolver', t, x0);
plot(t, x(:,1));
title('Position');
xlabel('time (sec)');
ylabel('x(t)');
ODESolver.m
function dx = ODESolver(t, x)
dx = zeros(2,1);
g = 0.15;
dx(1) = x(2);
dx(2) = -x(1) - g*x(2);
end
I've installed the odeint Library. My code for using runge_kutta4 is as follows
#include <iostream>
#include <boost/numeric/odeint.hpp>
using namespace std;
using namespace boost::numeric::odeint;
/* The type of container used to hold the state vector */
typedef std::vector< double > state_type;
const double gam = 0.15;
/* The rhs of x' = f(x) */
void lorenz( const state_type &x , state_type &dx , double t )
{
dx[0] = x[1];
dx[1] = -x[0] - gam*x[1];
}
int main(int argc, char **argv)
{
const double dt = 0.1;
runge_kutta_dopri5<state_type> stepper;
state_type x(2);
x[0] = 1.0;
x[1] = 0.0;
double t = 0.0;
cout << x[0] << endl;
for ( size_t i(0); i <= 100; ++i){
stepper.do_step(lorenz, x , t, dt );
t += dt;
cout << x[0] << endl;
}
return 0;
}
The result is in the following picture
My question is why the result varies? Is there something wrong with my C++ code?
These are the first values of both methods
Matlab C++
-----------------
1.0000 0.9950
0.9950 0.9803
0.9803 0.9560
0.9560 0.9226
0.9226 0.8806
0.8806 0.8304
0.8304 0.7728
0.7728 0.7084
0.7083 0.6379
Update:
The problem is that I forgot to include the initial value in my C++ code. Thanks for #horchler for noticing it. After including the proper values and using runge_kutta_dopri5 as #horchler suggested, the result is
Matlab C++
-----------------
1.0000 1.0000
0.9950 0.9950
0.9803 0.9803
0.9560 0.9560
0.9226 0.9226
0.8806 0.8806
0.8304 0.8304
0.7728 0.7728
0.7083 0.7084
I've updated the code to reflect these modifications.
The runge_kutta4 stepper in odeint is nothing like Matlab's ode45, which is an adaptive scheme based on the Dormand-Prince method. To replicate Matlab's results, you should probably try the runge_kutta_dopri5 stepper. Also, make sure that your C++ code uses the same absolute and relative tolerances as ode45 (defaults are 1e-6 and 1e-3, respectively). Lastly, it looks like you may not be saving/printing your initial condition in your C++ results.
If you're confused at why ode45 is not taking fixed steps even though you specified t = 0:0.1:10;, see my detailed answer here.
If you must use the fixed steprunge_kutta4 stepper, then you'll need to reduce the integration step-size in your C++ code to match Matlab's output.
The Matlab ode45 function already includes error control and I think also interpolation (dense output). to compare with boost.odeint you should use the same functionality there. Boost.odeint provides integrate functions that perform step-size control and dense output if the used stepper algorithm provides this functionality. The following code piece shows how you this is used with the default error control parameters from Matlab given by horchler:
#include <boost/numeric/odeint.hpp>
using namespace std;
using namespace boost::numeric::odeint;
/* The type of container used to hold the state vector */
typedef std::vector< double > state_type;
const double gam = 0.15;
/* The rhs of x' = f(x) */
void damped_osc( const state_type &x , state_type &dx , const double t )
{
dx[0] = x[1];
dx[1] = -x[0] - gam*x[1];
}
void print( const state_type &x, const double t )
{
cout << x[0] << endl;
}
int main(int argc, char **argv)
{
cout.precision(16); // full precision output
const double dt = 0.1;
typedef runge_kutta_dopri5<state_type> stepper_type;
state_type x(2);
x[0] = 1.0;
x[1] = 0.0;
integrate_const(make_dense_output<stepper_type>( 1E-6, 1E-3 ),
damped_osc, x, 0.0, 10.0, dt , print);
return 0;
}
Please note that the results might still not be exactly the same (as in all 16 digits) because the error control in Boost.odeint might not be impemented exactly as in Matlab's ode45.