I am trying to port the Harmonic Oscillator tutorial from ODEINT to Eigen, so that I could use VectorXd for state vectors. I cannot, however, make it compile.
I've been following some questions, for instance this is the closest except that I don't use any stepper here.
This is the code:
#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include <boost/numeric/odeint.hpp>
typedef Eigen::VectorXd state_type;
// was vector<double>
const double gam = 0.15;
/* The rhs of x' = f(x) defined as a class */
class harm_osc
{
public:
void operator() ( const state_type &x , state_type &dxdt , const double /* t */ )
{
dxdt(0) = x(1);
dxdt(1) = -x(0) - gam*x(1);
// dxdt[0] = x[1];
// dxdt[1] = -x[0] - gam*x[1];
}
};
/* The rhs of x' = f(x) */
void harmonic_oscillator(const state_type &x, state_type &dxdt, const double /* t */ )
{
dxdt(0) = x(1);
dxdt(1) = -x(0) - gam*x(1);
// dxdt[0] = x[1];
// dxdt[1] = -x[0] - gam*x[1];
}
void printer(const state_type &x , const double t)
{
// std::cout << t << "," << x[0] << "," << x[1] << std::endl;
std::cout << t << "," << x(0) << "," << x(1) << std::endl;
};
int main(int argc, const char * argv[])
{
state_type x(2);
x(0) = 1.0;
x(1) = 0.0;
// x[0] = 1.0;
// x[1] = 0.0;
std::cout << ">>>> FUNCTION" << std::endl;
// boost::numeric::odeint::integrate(harmonic_oscillator, x, 0.0, 10.0, 0.1, printer);
// boost::numeric::odeint::runge_kutta4<state_type, double, state_type, double, boost::numeric::odeint::vector_space_algebra> stepper();
boost::numeric::odeint::integrate<double, decltype(harmonic_oscillator), state_type, double, decltype(printer)>(harmonic_oscillator, x, 0.0, 10.0, 0.1, printer);
std::cout << ">>>> CLASS" << std::endl;
x(0) = 1.0;
x(1) = 0.0;
// x[0] = 1.0;
// x[1] = 0.0;
harm_osc ho;
boost::numeric::odeint::integrate<double, decltype(harmonic_oscillator), state_type, double, decltype(printer)>(ho, x, 0.0, 10.0, 0.1, printer);
return 0;
}
The compiler complains about No matching function for call to 'begin' in range_algebra.hpp from ODEINT in both class and function versions of integrate. As a matter of fact, Eigen matrices have no begin/end.
I've tried to play with the template parameters (as you see) with no avail.
Any hint?
Assertion failed using Eigen from repo
Using the latest Eigen from the repo (not the latest stable version), I can, as suggested, compile it and run. However, it fails an assertion in the integrate call tree:
Assertion failed: (index >= 0 && index < size()), function operator(), file eigen/include/eigen3/Eigen/src/Core/DenseCoeffsBase.h, line 427.
The call that fails is dxdt(0) = x(1); and subsequently in DenseCoeffsBase.h:
EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE Scalar&
operator()(Index index)
{
eigen_assert(index >= 0 && index < size()); // <---- INDEX IS 0, SIZE IS 0
return coeffRef(index);
}
Is it possible that ODEINT is trying to default-construct a VectorXd? I followed the path to my ODE call and dxdt is actually NULL:
(lldb) e dxdt
(state_type) $1 = {
Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > = {
m_storage = {
m_data = 0x0000000000000000
m_rows = 0
}
}
}
What is worse is that when using resizeLike to allow resizing dxdt, in the second step (so the first real computation of integrate) I have a x with NULL values:
(lldb) e dxdt
(state_type) $0 = {
Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > = {
m_storage = {
m_data = 0x0000000000000000
m_rows = 0
}
}
}
(lldb) e x
(state_type) $1 = {
Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > = {
m_storage = {
m_data = 0x0000000000000000
m_rows = 0
}
}
}
I found that ODEINT actually works fine with Eigen... only it is not documented, as far as I can see.
Digging around ODEINT's code, I have found a promising eigen.hpp header deep in the external directory.
And lo and behold, it works flawlessly:
#include <fstream>
#include <iostream>
#include <vector>
#include <boost/numeric/odeint/external/eigen/eigen.hpp>
#include <Eigen/Eigenvalues>
#define FMT_HEADER_ONLY
#include "fmt/core.h"
#include "fmt/format.h"
#include "fmt/format-inl.h"
#include "fmt/printf.h"
using namespace std;
int main(int argc, char *argv[])
{
Eigen::VectorXd v;
v.resize(2);
typedef Eigen::VectorXd state_type;
const double gam = 0.15;
v(0) = 1.0;
v(1) = 1.1;
auto harmonic_oscillator = [&](const state_type &x, state_type &dxdt, const double t)
{
dxdt[0] = x[1];
dxdt[1] = -x[0] - gam*x[1];
};
auto printer = [&](const state_type &x, const double t)
{
fmt::print(out, "time: {} state: {}\n", t, x);
};
odeint::integrate(harmonic_oscillator, v, 0.0 , 10.0 , 0.01, printer);
return 0;
}
Hope it helps others.
Related
I am testing with Eigen::VectorXd as state_type for boost::odeint. I use this code:
#include <Eigen/Eigen>
#include <boost/numeric/odeint.hpp>
#include <boost/numeric/odeint/external/eigen/eigen_algebra.hpp>
#include <iostream>
#include <vector>
template<class T>
struct push_back_state_and_time
{
std::vector<T>& m_states;
std::vector< double >& m_times;
push_back_state_and_time( std::vector<T> &states ,std::vector<double> × )
: m_states(states) , m_times(times) { }
void operator()(const T &x ,double t )
{
m_states.push_back(x);
m_times.push_back(t);
}
};
template<class T>
struct write_state
{
void operator() ( const T &x, const double t ) {
std::cout << t << "\t";
for(size_t i = 0; i < x.size(); ++i)
std::cout << x[i] << "\t";
std::cout << std::endl;
};
};
template<class T>
class odeClass {
private:
double Ka, Kel, Vd;
public:
odeClass(double ka, double kel, double vd) : Ka(ka), Kel(kel), Vd(vd) {};
void operator() (const T &x, T &dxdt, const double t) {
dxdt[0] = - Ka * x[0];
dxdt[1] = Ka * x[0] - Kel * x[1];
};
};
void testODE_Eigen() {
double Ka = 0.195, Vd = 13.8, Kel = 0.79 / Vd;
Eigen::VectorXd x(2);
x << 40 / Vd, 0.0;
odeClass<Eigen::VectorXd> myClass(Ka, Kel, Vd);
boost::numeric::odeint::runge_kutta4<Eigen::VectorXd, double, Eigen::VectorXd, double, boost::numeric::odeint::vector_space_algebra> stepper;
size_t steps = integrate_adaptive( stepper, myClass, x ,0.0 ,100.0 ,0.5 ,write_state<Eigen::VectorXd>() );
}
void testODE_Vector() {
double Ka = 0.195, Vd = 13.8, Kel = 0.79 / Vd;
std::vector<double> x = { 40 / Vd, 0.0 };
odeClass<std::vector<double>> myClass(Ka, Kel, Vd);
boost::numeric::odeint::runge_kutta4<std::vector<double>> stepper;
size_t steps = integrate_adaptive( stepper, myClass, x ,0.0 ,100.0 ,0.5 ,write_state<std::vector<double>>() );
}
int main()
{
testODE_Eigen();
return 0;
}
When running the function testODE_Vector();, it works perfectly, but when runningtestODE_Eigen();` I get the first integration step, one assertion stop: "Assertion failed: index >= 0 && index < size(), file C:\Eigen-3.3.7\Eigen\src\Core\DenseCoeffsBase.h, line 408.", and a windows message saying that the application has stop working, with no clue, if a use Code::Blocks. If I run the same on Visual Studio 2017 console application, I get one error saying Cannot write on a memory address without printing anything.
Any clues?
Thank you.
It looks like you are failing an assertion inside the Eigen library you are using. With a message like Assertion failed: index >= 0 && index < size() the library is probably trying to iterate over a vector internally and checks that the vector is valid before trying to access it. I would check the objects you are passing in and make sure they are valid.
It looks like one of the main differences in the two function testODE_Vector() and testODE_Eigen() is the way that you create that x. I'm not sure what this line
x << 40 / Vd, 0.0; is intended to do, but I would start there and verify that the value of x is right before it's passed into integrate_adaptive
My answer is a little late but in case someone else runs into this issue, here's what I found.
The issue seems to be that OdeInt can't handle properly the dynamic size with Eigen vectors and matrices. Therefore when creating dxdt, it creates an empty dynamic matrix or vector. This leads to an issue in your operator overload, where you try to access an element of dxdt where it contains none.
A quick fix I found was to use the resize() function (or conservativeResize()) to make sure dxdt has the proper size:
void operator() (const T &x, T &dxdt, const double t) {
dxdt.resize(x.size())
dxdt[0] = - Ka * x[0];
dxdt[1] = Ka * x[0] - Kel * x[1];
};
Note that if you want to use matrices instead of vectors you will have to use x.rows() and x.cols() instead of x.size().
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);
}
}
Is there a way to write the outputs of t and x of this example to a txt file instead of the console. Thanks!
This is the example I copied from Odeint website.
#include <iostream>
#include <boost/numeric/odeint.hpp>
using namespace std;
using namespace boost::numeric::odeint;
/* we solve the simple ODE x' = 3/(2t^2) + x/(2t)
* with initial condition x(1) = 0.
* Analytic solution is x(t) = sqrt(t) - 1/t
*/
void rhs( const double x , double &dxdt , const double t )
{
dxdt = 3.0/(2.0*t*t) + x/(2.0*t);
}
void write_cout( const double &x , const double t )
{
cout << t << '\t' << x << endl;
}
// state_type = double
typedef runge_kutta_dopri5< double > stepper_type;
int main()
{
double x = 0.0;
integrate_adaptive( make_controlled( 1E-12 , 1E-12 , stepper_type() ) ,
rhs , x , 1.0 , 10.0 , 0.1 , write_cout );
}
you can simply pipe the output of this example into a text file
$ ./lorenz > data.txt
Otherwise you can use a C++ ofstreams to write the output directly into a file, e.g. described there: http://www.cplusplus.com/doc/tutorial/files/
just replace cout with object of ofstream.
#include <iostream>
#include <fstream>
#include <boost/numeric/odeint.hpp>
using namespace std;
using namespace boost::numeric::odeint;
ofstream data("data.txt");
/* we solve the simple ODE x' = 3/(2t^2) + x/(2t)
* with initial condition x(1) = 0.
* Analytic solution is x(t) = sqrt(t) - 1/t
*/
void rhs(const double x, double &dxdt, const double t)
{
dxdt = 3.0 / (2.0*t*t) + x / (2.0*t);
}
void write_cout(const double &x, const double t)
{
data << t << '\t' << x << endl;
}
// state_type = double
typedef runge_kutta_dopri5< double > stepper_type;
int main()
{
double x = 0.0;
integrate_adaptive(make_controlled(1E-12, 1E-12, stepper_type()),
rhs, x, 1.0, 10.0, 0.1, write_cout);
}
I have an ODE of type:
x'(t) = a(t)x+g(t)
Which I am trying to solve. The only GSL ODE example isn't very helpful because the only coefficient (\mu) is not time dependent.
This question has been answered on the GSL mailing list however the answer is very unclear - g(t) is ignored and it has not been explained how to incorporate a(t) into func ( should it be passed in *params?).
Is there any example I can see where such an ODE is solved using GSL?
UPDATE: As has been pointed out below, this has been answered on the GSL mailing list. Here is a full example program of how this is done:
#include <stdio.h>
#include <math.h>
#include "gsl/gsl_errno.h"
#include "gsl/gsl_matrix.h"
#include "gsl/gsl_odeiv2.h"
int func(double t, const double y[], double f[], void *params) {
f[0] = -t* y[0];
return GSL_SUCCESS;
}
int jac(double t, const double y[], double *dfdy, double dfdt[], void
*params) {
gsl_matrix_view dfdy_mat = gsl_matrix_view_array(dfdy, 1, 1);
gsl_matrix * m = &dfdy_mat.matrix;
gsl_matrix_set(m, 0, 0, -t);
dfdt[0] = -1;
return GSL_SUCCESS;
}
int main(void) {
double mu = 0;
gsl_odeiv2_system sys = { func, jac, 1, &mu };
gsl_odeiv2_driver * d = gsl_odeiv2_driver_alloc_y_new(&sys,
gsl_odeiv2_step_rk1imp, 1e-7, 1e-7, 0.0);
int i;
double t = 0.0, t1 = 2.0;
const double x0 = 1.0;
double y[1] = {x0};
const int N = 100;
printf("time\t \tapprox solution \t exact solution\n");
for (i = 0; i <= N; i++) {
double ti = i * (t1 / N);
int status = gsl_odeiv2_driver_apply(d, &t, ti, y);
if (status != GSL_SUCCESS) {
printf("error, return value=%d\n", status);
break;
}
printf("%.5e\t%.5e\t\t%.5e\n", t, y[0], x0*exp(-0.5*t*t));
}
gsl_odeiv2_driver_free(d);
printf("\n...and done!\n");
return 0;
}
If you are not restricted to the GSL and/or C you can use http://odeint.com - a modern C++ library for ODEs. Odeint is part of boost, so it might be already installed on your system or can easily be installed be most of the package managers for Linux distributions.
You can simply define your coefficients and the ODE and use for example the RK4 method:
double coef_a( double t ) { /* return a(t) */ };
double coef_g( double t ) { /* return b(t) */ };
typedef std::array< double , 1 > state_type;
double ode( state_type const &x , state_type &dxdt , double )
{
dxdt[0] = coef_a( t ) * x[0] + coef_g( t );
}
state_type x;
double t_state , t_end , dt;
// initialize x
integrate_const( runge_kutta< state_type >() , ode , x , t_start , dt , t_end );
So I try next compilable code:
#include <math.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include "boolinq.h"
float dtSqrt(float x)
{
return sqrtf(x);
}
/// Returns the distance between two points.
/// #param[in] v1 A point. [(x, y, z)]
/// #param[in] v2 A point. [(x, y, z)]
/// #return The distance between the two points.
inline float dtVdist(const float* v1, const float* v2)
{
const float dx = v2[0] - v1[0];
const float dy = v2[1] - v1[1];
const float dz = v2[2] - v1[2];
return dtSqrt(dx*dx + dy*dy + dz*dz);
}
int main () {
float target[] = {1,2,3};
float floats1[] = {1.321f,2.123f,3.333f};
float floats2[] = {1.011f,2.234f,3.555f};
float floats3[] = {1.9f,2.9f,3.9f};
float floats4[] = {1,2,3};
float floats5[] = {1,2,3.123f};
std::vector<const float *> floatsVector;
floatsVector.push_back(floats1);
floatsVector.push_back(floats2);
floatsVector.push_back(floats3);
floatsVector.push_back(floats4);
floatsVector.push_back(floats5);
//stl way
//std::sort(floatsVector.begin(), floatsVector.end(), [&](const float* pointA, const float* pointB) -> bool{
// auto distA = dtVdist(pointA, target);
// auto distB = dtVdist(pointB, target);
// return distA < distB;
//});
// auto stl_point = floatsVector.front();
try {
auto point = boolinq::from( floatsVector )
.orderBy([&](const float* point) -> float{
auto dist = dtVdist(point, target);
return dist;
})
.reverse()
.toVector()
.front();
std::cout << point[0] << " " << point[1] << " " << point[2] << ";" << std::endl;
} catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
std::cin.get();
return 0;
}
having boolinq header this programm compiles quite fast. Yet it fails uncachably! at runtime with some inner vector assertion error:
problem is:
boolinq::from( floatsVector )
.orderBy([&](const float* point) -> float{
auto dist = dtVdist(point, target);
return dist;
}).toVector();
is empty vector.
uncommenting stl code makes programm work as expected!
futher more simple point reordering makes it work as expected:
float floats1[] = {1,2,3};
float floats2[] = {1,2,3.123f};
float floats3[] = {1.321f,2.123f,3.333f};
float floats4[] = {1.011f,2.234f,3.555f};
float floats5[] = {1.9f,2.9f,3.9f};
A really strange bug...
btw next code:
#include <math.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include "boolinq/boolinq.h"
struct Point {
float X;
float Y;
float Z;
Point() : X(0), Y(0), Z(0) {}
Point(float X, float Y, float Z) : X(X), Y(Y), Z(Z) {}
};
float dtSqrt(float x)
{
return sqrtf(x);
}
inline float dtVdist(const float* v1, const float* v2)
{
const float dx = v2[0] - v1[0];
const float dy = v2[1] - v1[1];
const float dz = v2[2] - v1[2];
return dtSqrt(dx*dx + dy*dy + dz*dz);
}
inline float dtVSqrDist(const Point & v1, const Point & v2)
{
const float dx = v2.X - v1.X;
const float dy = v2.Y - v1.Y;
const float dz = v2.Z - v1.Z;
return dx*dx + dy*dy + dz*dz;
}
int main () {
auto target = Point(1,2,3);
auto pointA = Point(1,-2,3);
auto pointB = Point(1,2,3);
std::vector<Point> pointsVector;
pointsVector.push_back(pointA);
pointsVector.push_back(pointB);
//stl way
std::sort(pointsVector.begin(), pointsVector.end(),
[&](const Point & pointA, const Point & pointB) -> bool{
auto distA = dtVSqrDist(pointA, target);
auto distB = dtVSqrDist(pointB, target);
return distA < distB;
});
std::reverse(pointsVector.begin(), pointsVector.end());
auto stl_point = pointsVector.front();
std::cout << "stl point: " << stl_point.X << " " << stl_point.Y << " " << stl_point.Z << ";" << std::endl;
//try {
auto points = boolinq::from( pointsVector )
.orderBy([&](const Point & point) -> float{
auto dist = dtVSqrDist(point, target);
return dist;
})
.reverse()
.toVector();
auto point = points.empty() ? Point() : points.front();
std::cout << "boolinq point: " << point.X << " " << point.Y << " " << point.Z << ";" << std::endl;
//} catch (std::exception &e) {
// std::cout << e.what() << std::endl;
//}
std::cin.get();
return 0;
}
Produces:
stl point: 1 -2 3;
boolinq point: 0 0 0;
What is wrong with my code? It looks alike orderBy examples thay provide on main boolinq page...?
Apparently boolinq does not support .orderBy().reverse() - which is somewhat worrying, granted. If you sort by the negative distance (without reverse), it works.
To me this looks like boolinq isn't ready for serious use.