I've been asked to solve this differential equation:
(x,y,vx,vy)'=(vx,vy,vy,-vx)
which should return a circular motion with a 2*pi period.
I implemented the function:
class FunzioneBase
{
public:
virtual VettoreLineare Eval(double t, const VettoreLineare& v) const = 0;
};
class Circonferenza: public FunzioneBase
{
private:
double _alpha;
public:
Circonferenza(double alpha) { _alpha = alpha; };
void SetAlpha(double alpha) { _alpha = alpha; };
virtual VettoreLineare Eval(double t, const VettoreLineare& v) const;
};
VettoreLineare Circonferenza::Eval(double t, const VettoreLineare& v) const
{
VettoreLineare y(4);
if (v.GetN() != 4)
{
std::cout << "errore" << std::endl;
return 0;
};
y.SetComponent(0, v.GetComponent(2));
y.SetComponent(1, v.GetComponent(3));
y.SetComponent(2, pow(pow(v.GetComponent(0), 2.) + pow(v.GetComponent(1), 2.), _alpha) * v.GetComponent(3));
y.SetComponent(3, - pow(pow(v.GetComponent(0), 2.) + pow(v.GetComponent(1), 2.), _alpha)) * v.GetComponent(2));
return y;
};
where _alpha equals to 0.
Now, this works just fine with Euler's method: if I integrate this ODE for 2 * pi * 10, given the initial condition (1, 0, 0, -1), with a 0.003 precision, I get that the position is comparable to (1, 0) within a range of 1 ± 0.1, as we should expect. But if I integrate the same ODE with Runge Kutta's Method (with a 0.003 precision, for 2 * pi * 10 seconds) implemented as follows:
class EqDifferenzialeBase
{
public:
virtual VettoreLineare Passo (double t, VettoreLineare& x, double h, FunzioneBase* f) const = 0;
};
class Runge_Kutta: public EqDifferenzialeBase
{
public:
virtual VettoreLineare Passo(double t, VettoreLineare& v, double h, FunzioneBase* f) const;
};
VettoreLineare Runge_Kutta::Passo(double t, VettoreLineare& v, double h, FunzioneBase* _f) const
{
VettoreLineare k1 = _f->Eval(t, v);
VettoreLineare k2 = _f->Eval(t + h / 2., v + k1 *(h / 2.));
VettoreLineare k3 = _f->Eval(t + h / 2., v + k2 * (h / 2.));
VettoreLineare k4 = _f->Eval(t + h, v + k3 * h);
VettoreLineare y = v + (k1 + k2 * 2. + k3 * 2. + k4) * (h / 6.);
return y;
}
the program returns an x position which equals to 0.39 aproximately, when the precision should theorically be, for a 4th order Runge Kutta's method, around 1E-6. I checked and found that the period, with Runge_Kutta's, seems to almost quadruplicate (since in a 2 * pi lapse, x gets from 1 to 0.48), but I don't understand why. This is the content of my main:
VettoreLineare DatiIniziali (4);
Circonferenza* myCirc = new Circonferenza(0);
DatiIniziali.SetComponent(0, 1.);
DatiIniziali.SetComponent(1, 0.);
DatiIniziali.SetComponent(2, 0.);
DatiIniziali.SetComponent(3, -1.);
double passo = 0.003;
Runge_Kutta myKutta;
for(int i = 0; i <= 2. * M_PI / passo; i++)
{
DatiIniziali = myKutta.Passo(0, DatiIniziali, passo, myCirc);
cout << DatiIniziali.GetComponent(0) << endl;
};
cout << 1 - DatiIniziali.GetComponent(0) << endl;
Thank you in advance.
Update: One error identified: Always compile with the -Wall option to catch all warnings and automatic code corrections of the compiler. Then you would have found
fff: In member function ‘virtual VettoreLineare Circonferenza::Eval(double, const VettoreLineare&) const’:
fff:xxx:114: error: invalid operands of types ‘void’ and ‘double’ to binary ‘operator*’
y.SetComponent(3, - pow(pow(v.GetComponent(0), 2.) + pow(v.GetComponent(1), 2.), _alpha)) * v.GetComponent(2));
^
where you are closing too early after _alpha so that the void of SetComponent gets to be a factor.
Update II: second error identified: In another post of yours the code of the linear vector class is given. There, in contrast to the addition (operator+), the scalar-vector product (operator*(double)) is modifying the calling instance. Thus in computing k2 the components of k1 get multiplied with h/2. But then this modified k1 (and also modified k2 and k3) are used in assembling the result y resulting in some almost completely useless state update.
Original rapid prototyping: I can tell you that a stripped down bare-bones implementation in python works flawlessly
import numpy as np
def odeint(f,t0,y0,tf,h):
'''Classical RK4 with fixed step size, modify h to fit
the full interval'''
N = np.ceil( (tf-t0)/h )
h = (tf-t0)/N
t = t0
y = np.array(y0)
for k in range(int(N)):
k1 = h*np.array(f(t ,y ))
k2 = h*np.array(f(t+0.5*h,y+0.5*k1))
k3 = h*np.array(f(t+0.5*h,y+0.5*k2))
k4 = h*np.array(f(t+ h,y+ k3))
y = y + (k1+2*(k2+k3)+k4)/6
t = t + h
return t, y
def odefunc(t,y):
x,y,vx,vy = y
return [ vx,vx,vy,-vx ]
pi = 4*np.arctan(1);
print odeint(odefunc, 0, [1,0,0,-1], 2*pi, 0.003)
ends with
(t, [ x,y,vx,vy]) = (6.2831853071794184,
[ 1.00000000e+00, -6.76088739e-15, 4.23436503e-12,
-1.00000000e+00])
as expected. You will need a debugger or intermediate output to find where the computation goes wrong.
Related
2nd task:
For a function f : R^n → R the gradient at a point ~x ∈ R^n is to be calculated:
- Implement a function
CMyVector gradient(CMyVector x, double (*function)(CMyVector x)),
which is given in the first parameter the location ~x and in the second parameter the function f as function pointer in the second parameter, and which calculates the gradient ~g = grad f(~x) numerically
by
gi = f(x1, . . . , xi-1, xi + h, xi+1 . . . , xn) - f(x1, . . . , xn)/h
to fixed h = 10^-8.
My currently written program:
Header
#pragma once
#include <vector>
#include <math.h>
class CMyVektor
{
private:
/* data */
int Dimension = 0;
std::vector<double>Vector;
public:
CMyVektor();
~CMyVektor();
//Public Method
void set_Dimension(int Dimension /* Aktuelle Dim*/);
void set_specified_Value(int index, int Value);
double get_specified_Value(int key);
int get_Vector_Dimension();
int get_length_Vektor();
double& operator [](int index);
string umwandlung()
};
CMyVektor::CMyVektor(/* args */)
{
Vector.resize(0, 0);
}
CMyVektor::~CMyVektor()
{
for (size_t i = 0; i < Vector.size(); i++)
{
delete Vector[i];
}
}
void CMyVektor::set_Dimension(int Dimension /* Aktuelle Dim*/)
{
Vector.resize(Dimension);
};
void CMyVektor::set_specified_Value(int index, int Value)
{
if (Vector.empty())
{
Vector.push_back(Value);
}
else {
Vector[index] = Value;
}
};
double CMyVektor::get_specified_Value(int key)
{
// vom intervall anfang - ende des Vectors
for (unsigned i = 0; i < Vector.size(); i++)
{
if (Vector[i] == key) {
return Vector[i];
}
}
};
int CMyVektor::get_Vector_Dimension()
{
return Vector.size();
};
// Berechnet den Betrag "länge" eines Vectors.
int CMyVektor::get_length_Vektor()
{
int length = 0;
for (size_t i = 0; i < Vector.size(); i++)
{
length += Vector[i]^2
}
return sqrt(length);
}
// [] Operator überladen
double& CMyVektor::operator [](int index)
{
return Vector[index];
}
main.cpp
#include <iostream>
#include "ClassVektor.h"
using namespace std;
CMyVektor operator+(CMyVektor a, CMyVektor b);
CMyVektor operator*(double lambda, CMyVektor a);
CMyVektor gradient(CMyVektor x, double (*funktion)(CMyVektor x));
int main() {
CMyVektor V1;
CMyVektor V2;
CMyVektor C;
C.set_Dimension(V1.get_length_Vector());
C= V1 + V2;
std::cout << "Addition : "<< "(";;
for (int i = 0; i < C.get_length_Vector(); i++)
{
std::cout << C[i] << " ";
}
std::cout << ")" << endl;
C = lamda * C;
std::cout << "Skalarprodukt: "<< C[0]<< " ";
}
// Vector Addition
CMyVektor operator+(CMyVektor a, CMyVektor b)
{
int ai = 0, bi = 0;
int counter = 0;
CMyVektor c;
c.set_Dimension(a.get_length_Vector());
// Wenn Dimension Gleich dann addition
if (a.get_length_Vector() == b.get_length_Vector())
{
while (counter < a.get_length_Vector())
{
c[counter] = a[ai] + b[bi];
counter++;
}
return c;
}
}
//Berechnet das Skalarprodukt
CMyVektor operator*(double lambda, CMyVektor a)
{
CMyVektor c;
c.set_Dimension(1);
for (unsigned i = 0; i < a.get_length_Vector(); i++)
{
c[0] += lambda * a[i];
}
return c;
}
/*
* Differenzenquotient : (F(x0+h)+F'(x0)) / h
* Erster Parameter die Stelle X - Zweiter Parameter die Funktion
* Bestimmt numerisch den Gradienten.
*/
CMyVektor gradient(CMyVektor x, double (*funktion)(CMyVektor x))
{
}
My problem now is that I don't quite know how to deal with the
CMyVector gradient(CMyVector x, double (*function)(CMyVector x))
function and how to define a function that corresponds to it.
I hope that it is enough information. Many thanks.
The function parameter is the f in the difference formula. It takes a CMyVector parameter x and returns a double value. You need to supply a function parameter name. I'll assume func for now.
I don't see a parameter for h. Are you going to pass a single small value into the gradient function or assume a constant?
The parameter x is a vector. Will you add a constant h to each element?
This function specification is a mess.
Function returns a double. How do you plan to turn that into a vector?
No wonder you're confused. I am.
Are you trying to do something like this?
You are given a function signature
CMyVector gradient(CMyVector x, double (*function)(CMyVector x))
Without knowing the exact definition I will assume, that at least the basic numerical vector operations are defined. That means, that the following statements compile:
CMyVector x {2.,5.,7.};
CMyVector y {1.,7.,4.};
CMyVector z {0.,0.,0.};
double a = 0.;
// vector addition and assigment
z = x + y;
// vector scalar multiplication and division
z = z * a;
z = x / 0.1;
Also we need to know the dimension of the CMyVector class. I assumed and will continue to do so that it is three dimensional.
The next step is to understand the function signature. You get two parameters. The first one denotes the point, at which you are supposed to calculate the gradient. The second is a pointer to the function f in your formula. You do not know it, but can call it on a vector from within your gradient function definition. That means, inside of the definition you can do something like
double f_at_x = function(x);
and the f_at_x will hold the value f(x) after that operation.
Armed with this, we can try to implement the formula, that you mentioned in the question title:
CMyVector gradient(CMyVector x, double (*function)(CMyVector x)) {
double h = 0.001;
// calculate first element of the gradient
CMyVector e1 {1.0, 0.0, 0.0};
double result1 = ( function(x + e1*h) - function(x) )/h;
// calculate second element of the gradient
CMyVector e2 {0.0, 1.0, 0.0};
double result2 = ( function(x + e2*h) - function(x) )/h;
// calculate third element of the gradient
CMyVector e3 {0.0, 0.0, 1.0};
double result3 = ( function(x + e3*h) - function(x) )/h;
// return the result
return CMyVector {result1, result2, result3};
}
There are several thing worth to mention in this code. First and most important I have chosen h = 0.001. This may like a very arbitrary choice, but the choice of the step size will very much impact the precision of your result. You can find a whole lot of discussion about that topic here. I took the same value that according to that wikipedia page a lot of handheld calculators use internally. That might not be the best choice for the floating point precision of your processor, but should be a fair one to start with.
Secondly the code looks very ugly for an advanced programmer. We are doing almost the same thing for each of the three dimensions. Ususally you would like to do that in a for loop. The exact way of how this is done depends on how the CMyVector type is defined.
Since the CMyVektor is just rewritting the valarray container, I will directly use the valarray:
#include <iostream>
#include <valarray>
using namespace std;
using CMyVektor = valarray<double>;
CMyVektor gradient(CMyVektor x, double (*funktion)(CMyVektor x));
const double h = 0.00000001;
int main()
{
// sum(x_i^2 + x_i)--> gradient: 2*x_i + 1
auto fun = [](CMyVektor x) {return (x*x + x).sum();};
CMyVektor d = gradient(CMyVektor{1,2,3,4,5}, fun);
for (auto i: d) cout << i<<' ';
return 0;
}
CMyVektor gradient(CMyVektor x, double (*funktion)(CMyVektor x)){
CMyVektor grads(x.size());
CMyVektor pos(x.size());
for (int i = 0; i<x.size(); i++){
pos[i] = 1;
grads[i] = (funktion(x + h * pos) - funktion(x))/ h;
pos[i] = 0;
}
return grads;
}
The prints out 3 5 7 9 11 which is what is expected from the given function and the given location
I just started using boost today and found this post to be extremely helpful. I am attempting to use boost::bisect to solve a parametric equation for a series of values. The following works if I want to solve for a value of 0.8:
#include <boost/math/tools/roots.hpp>
//declare constants
const double Y_r = 0.2126;
const double Y_g = 0.7152;
const double Y_b = 0.0722;
struct FunctionToApproximate {
double operator() (double x) {
return (pow (x, 2.4) * Y_r) + (pow ((x*0.6)+0.4, 2.4) * Y_g) + (1 * Y_b) - 0.8;
}
};
struct TerminationCondition {
bool operator() (double min, double max) {
return fabs(min - max) <= t_c;
}
};
using boost::math::tools::bisect;
std::pair<double, double> result = bisect(FunctionToApproximate(), 0.0, 1.0, TerminationCondition());
double root = (result.first + result.second) / 2;
I would like to wrap this in a loop so I could solve for values other than 0.8. How would I go about this?
Many thanks!
You can add a state/data member to FunctionToApproximate to hold the value that you want to substract in each call:
struct FunctionToApproximate {
FunctionToApproximate(double val) :val(val){}
double operator() (double x) {
return (pow (x, 2.4) * Y_r) + (pow ((x*0.6)+0.4, 2.4) * Y_g) + (1 * Y_b) - val;
}
double val;
};
Then wrap the calculations inside a loop should be straight forward.
for(double i = 0.1; i <= 1.0; i+=1.0) {
std::pair<double, double> result = bisect(FunctionToApproximate(i), 0.0, 1.0, TerminationCondition());
double root = (result.first + result.second) / 2;
}
I have this C++ program:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <cmath>
using namespace std;
double dx2(int t, int x, int dx)
{
return (-9.8*cos(x));
}
int square(int x)
{
return (x*x);
}
double RK4(float t, float x, float dx, float h)
{
double k1, k2, k3, k4, l1, l2, l3, l4, diff1, diff2;
k1 = h*dx2(t,x,dx);
l1 = h*k1;
k2 = h*dx2(t+h/2,x+l1/2,dx+k1/2);
l2 = h*k2;
k3 = h*dx2(t+h/2,x+l2/2,dx+k2/2);
l3 = h*k3;
k4 = h*dx2(t+h,x+l3,dx+k3);
l4 = h*k4;
diff1 = (l1+2*l2+2*l3+l4)/float(6);
diff2 = (k1+2*k2+2*k3+k4)/float(6);
double OUT[] = {diff1, diff2};
return OUT;
}
int main()
{
double diff, t, t0, t1, x, x0, dx, dx0, h, N;
N = 1000;
t0 = 0;
t = t0;
t1 = 10;
x0 = 0;
x = x0;
dx0 = 0;
dx = dx0;
h = (t1 - t0) / float(N);
for(int i = 1; i<=N; i++) {
diff = RK4(t,x,dx,h);
x = x + diff;
t = t + h;
}
cout << diff;
return 0;
}
As you can see in this program I am solving the 2nd-order differential equation (if there is a way to insert LaTeX equations into my question please tell me):
d2x/dt2= -9.8 cos(x)
which is an example of the simple pendulum's equations of motion. The problem lines are 33 and 34. In it I am attempting to define the first element of the OUT array as diff1 and the second element as diff2. Whenever I compile this program (named example.cpp) I get the error:
g++ -Wall -o "example" "example.cpp" (in directory: /home/fusion809/Documents/CodeLite/firstExample)
example.cpp: In function ‘double RK4(float, float, float, float)’:
example.cpp:33:9: error: cannot convert ‘double*’ to ‘double’ in return
return OUT;
^~~
Compilation failed.
Exactly, since you're returning an array of double's, that decays to double*, but the function is defined to return double. An array of type T and the type T are different types in C++, and they can't be converted between, generally speaking.
In this case, you might be better off with a std::pair<T1, T2> (#include <utility>) since you're using C++ and the standard library, or a structure with two fields of type double. Look up std::pair<> and std::tie<>, the former being used to make pairs of elements of different types, and the latter being used to make tuples of different types of arbitrary size.
When you write the std::pair's elements to std::cout, use the first, second members to access the pair's fields. A std::pair can't be directly output using the overloaded stream operator for std::cout.
Edit:
#include <utility>
std::pair<double, double> RK4(float t, float x, float dx, float h)
{
/* snip */
diff1 = (l1+2*l2+2*l3+l4)/float(6);
diff2 = (k1+2*k2+2*k3+k4)/float(6);
return {diff1, diff2};
}
int main()
{
double x, dx;
/* snip */
for(int i = 1; i<=N; i++) {
std::pair<double, double> diff = RK4(t,x,dx,h);
// or use with C++11 and above for brevity
auto diff = RK4(t,x,dx,h);
x = x + diff.first;
dx = dx + diff.second;
t = t + h;
}
cout << x << " " << dx << "\n" ;
return 0;
}
The return type of your RK4 function is double, which is a single value, but you're trying to return an array of two of them. That won't work. You could change the return type to double* and use new double[2] to allocate an array, but it'd be simpler and safer to use std::pair<double, double> as the return type. Then you can just do return { diff1, diff2 };.
To return several values from function you have several choice:
as all you returned type are identical, you may return array:
std::array<double, 2> RK4(float t, float x, float dx, float h)
{
// ...
return {{diff1, diff2}};
}
or std::vector
std::vector<double> RK4(float t, float x, float dx, float h)
{
// ...
return {{diff1, diff2}};
}
You may return std::tuple or std::pair (limited to 2 elements):
std::pair<double, double> RK4(float t, float x, float dx, float h)
{
// ...
return {{diff1, diff2}};
}
or
std::tuple<double, double> RK4(float t, float x, float dx, float h)
{
// ...
return {{diff1, diff2}};
}
You may also create a custom class
struct RK4Result
{
double diff1;
double diff2;
};
RK4Result RK4(float t, float x, float dx, float h)
{
// ...
return {diff1, diff2};
}
And for type expensive to move, you may use any previous method, but by out parameters:
struct RK4Result
{
double diff1;
double diff2;
};
void RK4(float t, float x, float dx, float h, RK4Result& res)
{
// ...
res = {diff1, diff2};
}
I'm fairly new with C++ and I'm trying to use it via Rcpp to speed up my R code.
The below code integrates from from t0 to t1- this is done in the "lorenz" function. Test4 integrates using "lorenz" "counts" number of times. However at time "t1" the state of the system is modified in "write_lorenz" before the system is rerun and this is where the problem is. If I run the same program over and over again by calling test4 from R, printing to the screen always produces the same result, however, my returned matrix "u" does not, and seems to eventually converge to whatever "t1" is which is the problem.
My input values don't change so I'm wondering if there a memory leak, or if something else is going on, how to fix it.
Also I'm wondering if my initialization of "u" is incorrect and I should be using the "new" command.
What I tried was
NumericMatrix* u = NULL;
*u = new NumericMatrix;
and then I tried accessing the elements of the matrix as *u(1,2) for instance, but accessing the elements this way caused an error saying u was not a function.
Any help would be greatly appreciated
I modified this code from the following site
http://headmyshoulder.github.io/odeint-v2/examples.html
so I could use it with Rcpp
//###################################R Code ###############################
library(Rcpp)
sourceCpp("test4.cpp")
sigma <- 10.0
R <-28.0
b <- 8.0 / 3.0
a2 <- c(10.0 , 1.0 , 1.0) #initial conditions X0,I0,Y0
L2 <- c(0.0 , 2.0 , 0.1) #initial time, kick time, error
counts <- 2
kick <-1.0; # kick size
pars <-c(sigma,R,b,kick)
test4(a=a,L2=L2,counts=counts,pars= pars)
// C ++ code
//[[Rcpp::depends(BH)]]
//[[Rcpp::depends(RcppEigen)]]
//[[Rcpp::plugins("cpp11")]]
#include <Rcpp.h>
#include <RcppEigen.h>
#include <math.h>
#include <boost/array.hpp>
#include <boost/numeric/odeint.hpp>
#include <boost/algorithm/string.hpp>
using namespace boost::numeric::odeint;
using namespace std;
using namespace Rcpp;
using namespace Eigen;
double sigma =0;
double e =0;
double b =0 ;
double t0 =0;
double t1 = 0;
double kick =0;
double X0 = 0;
double I0 =0;
double Y0 =0;
double N = 0;
int counter =0;
typedef boost::array< double , 3 > state_type;
NumericMatrix u(4,5);
void lorenz( const state_type &x , state_type &dxdt , double t )
{
dxdt[0] = sigma * ( x[1] - x[0] );
dxdt[1] = e * x[0] - x[1] - x[0] * x[2];
dxdt[2] = -b * x[2] + x[0] * x[1];
}
void write_lorenz( const state_type &x , const double t )
{
if(t==t1){
X0 = x[0];
I0 = x[1];
Y0 = x[2];
N = X0+I0+Y0;
X0 = X0 + exp(kick*N);
counter++;
//for some reason cout and u don't match for multiple runs of the
//program
cout << t << '\t' << X0 << '\t' << I0 << '\t' << Y0 << endl;
u(counter,0) = t;
u(counter,1) = X0;
u(counter,2) = I0;
u(counter,3) = Y0;
}
}
// [[Rcpp::export]]
NumericMatrix test4(NumericVector a, NumericVector L2,int counts,NumericVector pars)
{
double error; // control integration error
// initialize model parameters
//maybe these should be parenthesis?
sigma = pars[0]; //# average per capita birh rate 1-10(the mean is 4.7)
e = pars[1]; // # per capita average growth rate
b= pars[2];
kick = pars[3]; // kick size
//cout << sigma << R << b << kick << endl;
//myfile.open (ST);
X0 = a[0]; I0 =a[1]; Y0 = a[2];
int i = 0;
t0 = L2[0];
t1 = L2[1];
error = L2[2];
u(0,0) = t0;
u(0,1) = X0;
u(0,2) = I0;
u(0,3) = Y0;
// initial conditions
for(i=0;i<counts;i++)
{
state_type x = { X0 , I0 , Y0 };
integrate( lorenz , x , t0 , t1 , error , write_lorenz );
}
return u; // the variable I hope will be global
}
Here is a simple adaptation of the pure C++ file you linked to. We simply define a struct of three vectors into which we push the elements of each iterations--as opposed to printing them to standard output.
For data structures that grow, we prefer C++ standard library types (as our types have to be like R types, their internals do not match well to increasing one-by-one which is expensive for them). So at the end, we just copy into an R data.frame.
#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;
const double sigma = 10.0, r = 28.0, b = 8.0 / 3.0;
typedef boost::array< double , 3 > state_type;
void lorenz( const state_type &x , state_type &dxdt , double t ) {
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];
}
struct foo { std::vector<double> a, b, c; };
struct foo f;
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() {
state_type x = { 10.0 , 1.0 , 1.0 }; // initial conditions
integrate( lorenz , x , 0.0 , 25.0 , 0.1 , append_lorenz );
return DataFrame::create(Named("a") = f.a,
Named("b") = f.b,
Named("c") = f.c);
}
/*** R
res <- callMain()
print(head(res))
*/
Here is the output of the R code (intentially limited to fist few rows):
R> sourceCpp("/tmp/lorenz.cpp")
R> res <- callMain()
R> print(head(res))
a b c
1 10.00000 1.00000 1.00000
2 9.40816 2.99719 1.12779
3 8.92164 5.35684 1.46991
4 8.68193 7.82671 2.05762
5 8.73730 10.42718 2.94783
6 9.11080 13.10452 4.18849
R>
I have written a program to approximate the solutions to ordinary differential equations using Adam's Method.
Running the program with gdb gives me:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff5f3ffff8
0x0000000100003977 in std::vector<double, std::allocator<double> >::push_back (this=0x100005420, __x=#0x100005310) at stl_vector.h:604
604 this->_M_impl.construct(this->_M_impl._M_finish, __x);
Clearly something is wrong with my treatment of vector.push_back, but I do not know where to begin looking. I can not think of a case where modifying a vector is illegal.
Call differentiate() to begin. Mathematics is done in step(). Adaptive time advancing across an interval with advance(). Check the chosen time step with checkdt() before running step() again.
Sorry for the huge code dump. I am sure many improvements can be made purely from the standpoint of C++, without knowledge of the mathematics:
//============================================================================
// Description : An Adam's Method Ordinary Differential Equation Approximation
// Disclaimer : Posted to StackOverflow for help on a segmentation fault
//============================================================================
#include <iostream> //IO
#include <vector> //std::vector
#include <cmath> //abs, pow, sqrt
#include <numeric> //accumulate
using namespace std;
/* Terminology:
* f(t, y) = the ordinary differential equation that will be solved
* y(t) = solution of f at point t.
* told = the previous point at which f was evaluated and solved
* tnow = the current point at which f is evaluated and solved
* tnew = the new (interesting) point at which f will be evaluated and solved
*
* Yold = the corrected solution of the differential equation at told
* Ynow = the corrected solution of the differential equation at tnow
* Ynew = the corrected solution of the differential equation at tnew
*
* fold = the value of the given differential equation at told
= f(told, Yold)
* fnow = the value of the given differential equation at tnow
= f(tnow, Ynow)
* fnew = the value of the given differential equation at tnew
= f(tnew, Ynew)
*
* Pnew = prediction for the value of Ynew
* dt = abs(tnew - tnow)
* dtold = abs(tnow - told)
*/
//Information storage
struct simTime {
double told;
double tnow;
double tnew;
double dt;
double dtold;
double tol;
double agrow;
double ashrink;
double dtmin;
double dtmax;
double endTime;
double fold;
double fnow;
double fnew;
double Yold;
double Ynow;
double Ynew;
double Pnew;
int stepsSinceRejection;
int stepsRejected;
int stepsAccepted;
} test;
//Define global variables
std::vector<double> errorIndicators(0);
std::vector<double> solutions(0);
std::vector<double> differencesDDY(0);
std::vector<double> differencesDDYSquared(0);
std::vector<double> timesTNew(0);
//Function declarations
void step(double fnow, double fold, double Ynow, double tnew, double tnow,
double dtold, double dt, double(*f)(double t, double y), double told);
void advance();
void shiftvariables();
void printvector(std::vector<double>& vec);
void differentiate(double(*f)(double t, double y), double y0, double a,
double b);
double f(double t, double y);
void checkdt();
int main() {
differentiate(f, 0, 1, 5);
cout << "Time values:" << endl;
printvector(timesTNew);
cout << "Solutions:" << endl;
printvector(solutions);
cout << "Differences between Prediction and Solution:" << endl;
printvector(differencesDDY);
return 0;
}
//Shift back all the variables to make way for the new values
void shiftvariables() {
test.tnow = test.tnew;
test.dtold = test.dt;
test.Yold = test.Ynow;
test.Ynow = test.Ynew;
test.fold = test.fnow;
test.fnow = test.fnew;
advance();
}
//Ordinary differential equation to be solved
double f(double t, double y) {
return pow(t, 2);
}
//Calculate the predicted and corrected solution at a chosen tnew
void step(double fnow, double fold, double Ynow, double tnew, double tnow,
double dtold, double dt, double(*f)(double t, double y), double told) {
//The calculation for Ynew requires integration. I first thought I would need to
// use my project 1 code to calculate the integration, but now I see in class we
// solved it analytically such that integration is not required:
//Linear prediction of Ynew using Ynow and fnow
double Pnew = Ynow + (dt * fnow) + (dt * dt / (2 * dtold)) * (fnow - fold);
test.Pnew = Pnew;
//Predict the value of f at tnew using Pnew
double fnew = f(tnew, Pnew);
test.fnew = fnew;
//Calculate the corrected solution at tnew
double interpolationFactor = fnew - (fnow + dt * (fnow - fold) / dtold);
double integration = (dt / 6) * (2 * dt + 3 * dtold) / (dt + dtold);
double Ynew = Pnew + interpolationFactor * integration;
test.Ynew = Ynew;
//Update the variables for the next round
shiftvariables();
}
//Check the previous solution and choose a new dt to continue evaluation
void advance() {
//The error indicator is the l2-norm of the prediction minus the correction
double err_ind = sqrt(
std::accumulate(differencesDDYSquared.begin(),
differencesDDYSquared.end(), 0));
errorIndicators.push_back(err_ind);
// Case where I reject the step and retry
if (err_ind > test.tol && test.dt > test.dtmin) {
++test.stepsRejected;
test.stepsSinceRejection = 0;
test.dt = test.dt * 0.5;
test.tnew = test.tnow + test.dt;
checkdt();
}
// Cases where I accept the step and move forward
else {
++test.stepsAccepted;
++test.stepsSinceRejection;
solutions.push_back(test.Ynew);
differencesDDY.push_back(abs(test.Pnew - test.Ynew));
differencesDDYSquared.push_back(pow((test.Pnew - test.Ynew), 2));
//Decrease dt
if (err_ind >= 0.75 * test.tol) {
test.dtold = test.dt;
test.dt = (test.dt * test.ashrink);
test.tnew = test.tnow + test.dt;
checkdt();
}
//Increase dt
else if (err_ind <= test.tol / 4) {
if ((test.stepsRejected != 0) && (test.stepsSinceRejection >= 2)) {
test.dt = (test.dt * test.agrow);
test.tnew = test.tnow + test.dt;
checkdt();
} else if (test.stepsRejected == 0) {
test.dt = (test.dt * test.agrow);
test.tnew = test.tnow + test.dt;
checkdt();
}
}
}
}
//Check that the dt chosen by advance is acceptable
void checkdt() {
if ((test.tnew < test.endTime) && (test.endTime - test.tnew < test.dtmin)) {
cout << "Reached endTime." << endl;
} else if (test.dt < test.dtmin) {
test.dt = test.dtmin;
test.tnew = test.tnow + test.dt;
timesTNew.push_back(test.tnew);
step(test.fnow, test.fold, test.Ynow, test.tnew, test.tnow, test.dtold,
test.dt, f, test.told);
} else if (test.dt > test.dtmax) {
test.dt = test.dtmax;
test.tnew = test.tnow + test.dt;
timesTNew.push_back(test.tnew);
step(test.fnow, test.fold, test.Ynow, test.tnew, test.tnow, test.dtold,
test.dt, f, test.told);
} else if ((test.tnew + test.dt) > test.endTime) {
test.dt = test.endTime - test.tnew;
test.tnew = test.tnow + test.dt;
checkdt();
} else if (((test.tnew + test.dt) < test.endTime)
&& ((test.tnew + 2 * test.dt) > test.endTime)) {
test.dt = (test.endTime - test.tnew) / 2;
test.tnew = test.tnow + test.dt;
checkdt();
}
//If none of the above are satisfied, then the chosen dt
// is ok and proceed with it
else {
timesTNew.push_back(test.tnew);
step(test.fnow, test.fold, test.Ynow, test.tnew, test.tnow, test.dtold,
test.dt, f, test.told);
}
}
//meta function to solve a differential equation, called only once
void differentiate(double(*f)(double t, double y), double y0, double a,
double b) {
//Set the starting conditions for the solving of the differential equation
test.fnow = f(a, y0);
test.endTime = b;
test.Ynow = y0;
//solutions.push_back(y0);
timesTNew.push_back(a);
//Set the constants
test.ashrink = 0.8;
test.agrow = 1.25;
test.dtmin = 0.05;
test.dtmax = 0.5;
test.tol = 0.1;
//Set fold = fnow for the first step
test.fold = test.fnow;
test.tnow = a;
test.told = a - test.dtmin;
test.dtold = abs(test.tnow - test.told);
//Create the first prediction, which will then lead to correcting it with step
advance();
}
// Takes a vector as its only parameters and prints it to stdout
void printvector(std::vector<double>& vec) {
for (vector<double>::iterator it = vec.begin(); it != vec.end(); ++it) {
cout << *it << ", ";
}
cout << "\n";
}
Thank you.
Since you're using recursion, could it be possible that you are running out of stack memory, thus causing the segfault? This could happen if either your app recurses too many times or if some bug causes it to recurse infinitely.
Note, as sth suggests in a comment, a debugger may help you decide whether or not this is the case.