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.
Related
I need implement the class Option_Pricer that encapsulates all the functions relevant to price both call and put options. The teacher is giving me a code listing (.cpp file) that I have to turn into a class. All the functions that I used in my class are therefore coming from the teacher. I simply have to implement them as a class.
Here is what I have done so far: I have split the code into two different files. One is called option_pricer.hpp and is used as an header for the main file option_pricer.cpp.
//option_pricer.hpp
#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
class Option_Pricer {
private:
void init();
public:
double S;
double K;
double r;
double v;
double T;
double x;
double j;
public:
//Constructors
call_price();
put_price();
norm_pdf();
norm_cdf();
d_j() const;
// Assignment operator
call_price& operator = (const call_price& call);
put_price& operator = (const put_price& put);
};
Here is the main file:
//option_pricer.cpp
#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
#include "option_pricer.hpp"
double Option_Pricer::norm_pdf(const double& x) const {
return (1.0/(pow(2*M_PI,0.5)))*exp(-0.5*x*x);
}
double Option_Pricer::norm_cdf(const double& x) const {
double k = 1.0/(1.0 + 0.2316419*x);
double k_sum = k*(0.319381530 + k*(-0.356563782 + k*(1.781477937 + k*(-1.821255978 + 1.330274429*k))));
if (x >= 0.0) {
return (1.0 -(1.0/(pow(2*M_PI,0.5)))*exp(-0.5*x*x) * k_sum);
}
else {
return 1.0 - norm_cdf(-x);
}
}
double Option_Pricer::d_j(const int& j, const double& S, const double& K, const double& r, const double& v, const double& T) const {
return (log(S/K) + (r + (pow(-1,j 1))*0.5*v*v)*T)/(v*(pow(T,0.5)));
}
double Option_Pricer::call_price(const double& S, const double& K, const double& r, const double& v, const double& T) const {
return S * norm_cdf(d_j(1, S, K, r, v, T))-K*exp(-r*T) * norm_cdf(d_j(2, S, K, r, v, T));
}
double Option_Pricer::put_price(const double& S, const double& K, const double& r, const double& v, const double& T) const {
return -S*norm_cdf(-d_j(1, S, K, r, v, T))+K*exp(-r*T) * norm_cdf(-d_j(2, S, K, r, v, T));
}
int main() {
Option_Pricer p;
p.S = 100.0;
p.K = 100.0;
p.r = 0.05;
p.v = 0.2;
p.T = 1.0;
double call_price = p.call_price();
double call_put = p.put_price();
// Finally we output the parameters and prices
std::cout << "Underlying: " << p.S << std::endl;
std::cout << "Strike: " << p.K << std::endl;
std::cout << "Risk-Free Rate: " << p.r << std::endl;
std::cout << "Volatility: "<< p.v << std::endl;
std::cout << "Maturity: " << p.T << std::endl;
std::cout << "Call price: " << call_price << std::endl;
std::cout << "Put price: " << call_put << std::endl;
return 0;
}
However, as you can guess, my code isn't compiling really well. My most common error is the following:
option_pricer.cpp:7:8: error: no declaration matches ‘double Option_Pricer::norm_pdf(const double&) const’
7 | double Option_Pricer::norm_pdf(const double& x) const {
| ^~~~~~~~~~~~~
I don't understand how I should call the norm_pdf from outside of the header (same question for norm_cdf and d_j).
I'm fairly new to C++ (was using Python before) and therefore don't understand yet how am I supposed to access the variables (S, K,...) from outside of my class.
Help will be appreciated! Thank you!
You need to make and understand the distinction between a class and an object. Very simply, an object is a collection of values in memory, and a class is a description of those values and of code that will use data organized according to the class description.
So, since Option_Pricer is a class, it doesn't make sense to say Option_Pricer.S = 100.0; in your main() method. You need to create an object of type Option_Pricer, and then fill that object's memory region with the values you want. A common method for doing that - especially in your case where you are simply initializing the object with numeric data - is to create and use a constructor, although you could modify your init() method to take arguments and set values and that would be fine too. You can even set the values one-by-one as you have done, since you made the values public, but you have to modify the object, not the class.
Ex.
int main()
{
Option_Pricer p(100.0, 100.0, 0.5, 0.2, 1.0);
double call_price = p.call_price();
// or
Option_Pricer p2;
p2.init(100.0, 100.0, 0.5, 0.2, 1.0);
double call_price2 = p2.call_price();
// or, if you like typing or want the meaning of the numbers to be super clear
Option_Pricer p3;
p3.S = 100.0;
p3.K = 100.0;
p3.r = 0.05;
p3.v = 0.2;
p3.T = 1.0;
// ...
This doesn't address everything that's wrong with your code, but I'd start by addressing the above. I think the problems that others are pointing out will be easier to sort out once you get the concept of an object squared away.
The problem
I implemented velocity verlet algorithm to compute trajectories of 2 bodies interacting with each other gravitationally (newtonian gravity only). Orbiting smaller body has a very small mass, body that is in the center of the orbit has a large mass.
In theory Velocity Verlet should not change total energy of the system (it will oscilate but over time the average will remain close to the initial energy).
However in practice I observed increase of energy over time.
Results
Here are some results which illustrate the problem.
All simulations were performed with a timestep dt=0.001.
Orbited body had a mass of 1000 and gravitational constant of the universe was set to G=1.0
In all cases smaller body initial position was {0, 0, 1} and it's initial velocity was {0, 32, 0}.
Initial velocity of larger body was {0,0,0}.
Case 1 (small body mass = 0.00001)
Here is the trajectory of the smaller body:
And here is energy over 100k steps.
As we can see the energy does not change by a lot. Small changes are likely due to inaccuracies in the calculations.
Case 1 (small body mass = 0.001)
Here is trajectory of the orbiting body:
And here is total energy:
As we can see now system is gaining energy.
Case 3 (small body mass = 1)
Here is trajectory of the orbiting body:
And here is total energy:
Now we are gaining a lot of energy.
code
Here is the source code that is performing all calculations:
Code for advancing integrator:
void Universe::simulation_step()
{
for(std::size_t i=0; i<get_size(); i++)
{
// Verlet step 1: Compute v(t + dt/2) = v(t) + 0.5*dt*a(t)
const Vector3D<Real> vel_half_step = {
velocity(i, 0) + static_cast<Real>(0.5)*sim_config.timestep*acceleration(i, 0),
velocity(i, 1) + static_cast<Real>(0.5)*sim_config.timestep*acceleration(i, 1),
velocity(i, 2) + static_cast<Real>(0.5)*sim_config.timestep*acceleration(i, 2)
};
// Verlet step 2: Compute x(t + dt) = x(t) + v(t + dt/2)*dt
position(i, 0) += vel_half_step.x*sim_config.timestep;
position(i, 1) += vel_half_step.y*sim_config.timestep;
position(i, 2) += vel_half_step.z*sim_config.timestep;
// Verlet step 3: update forces and update acceleration.
const Vector3D<Real> forces = compute_net_grawitational_force(i);
acceleration(i, 0) = forces.x/mass(i);
acceleration(i, 1) = forces.y/mass(i);
acceleration(i, 2) = forces.z/mass(i);
// Verlet step 4: update velocity to the full timestep.
velocity(i, 0) = vel_half_step.x + static_cast<Real>(0.5)*sim_config.timestep*acceleration(i, 0);
velocity(i, 1) = vel_half_step.y + static_cast<Real>(0.5)*sim_config.timestep*acceleration(i, 1);
velocity(i, 2) = vel_half_step.z + static_cast<Real>(0.5)*sim_config.timestep*acceleration(i, 2);
}
sim_time += sim_config.timestep;
}
Here is code for computing net gravitational force acting on the body:
Vector3D<Real> Universe::compute_net_grawitational_force(std::size_t i)
{
Vector3D<Real> accumulated_force = {0,0,0};
const Vector3D<Real> r2 = {
position(i, 0),
position(i, 1),
position(i, 2)
};
const Real m1 = mass(i);
for(std::size_t k=0; k<get_size(); k++)
{
if(k == i)
continue;
const Vector3D<Real> distace_vec = {
r2.x - position(k, 0),
r2.y - position(k, 1),
r2.z - position(k, 2),
};
const Real distance = distace_vec.norm2();
// Compute term that will be multipled by distance vector.
const Real a = (-1*sim_config.G*m1*mass(k))/
(distance*distance*distance);
// Compute and add new force acting on the body.
accumulated_force.x += distace_vec.x*a;
accumulated_force.y += distace_vec.y*a;
accumulated_force.z += distace_vec.z*a;
}
return accumulated_force;
}
Here is code that implements norm2():
template<typename T>
struct Vector3D
{
T x;
T y;
T z;
T norm2() const
{
return sqrt(x*x + y*y + z*z);
}
};
Finally here is code that computes results plotted previously:
std::vector<Real> x, y, z, energy;
x.resize(NSTEPS);
y.resize(NSTEPS);
z.resize(NSTEPS);
energy.resize(NSTEPS);
for(std::size_t i=0; i<NSTEPS; i++)
{
universe.simulation_step();
const Vector3D<Real> pos1 =
{
universe.get_positions()(0, 0),
universe.get_positions()(0, 1),
universe.get_positions()(0, 2)
};
const Vector3D<Real> pos2 =
{
universe.get_positions()(1, 0),
universe.get_positions()(1, 1),
universe.get_positions()(1, 2)
};
x[i] = pos2.x;
y[i] = pos2.y;
z[i] = pos2.z;
// Compute total kinetic energy of the system.
const Vector3D<Real> vel1 =
{
universe.get_velocities()(0, 0),
universe.get_velocities()(0, 1),
universe.get_velocities()(0, 2),
};
const Vector3D<Real> vel2 =
{
universe.get_velocities()(1, 0),
universe.get_velocities()(1, 1),
universe.get_velocities()(1, 2),
};
const Real mass1 = universe.get_masses()(0);
const Real mass2 = universe.get_masses()(1);
const Real spd1 = vel1.norm2();
const Real spd2 = vel2.norm2();
energy[i] = (spd1*spd1)*mass1*static_cast<float>(0.5);
energy[i] += (spd2*spd2)*mass2*static_cast<float>(0.5);
// Compute total potential energy
const Vector3D<Real> distance_vec =
{
pos1.x - pos2.x,
pos1.y - pos2.y,
pos1.z - pos2.z
};
const Real G = universe.get_sim_config().G;
energy[i] += -G*((mass1*mass2)/distance_vec.norm2());
}
Type Real is float.
My theories
I'm a beginner when it comes to numerical integration (that's why I posted this question here).
However here are some theories about what might be wrong:
There is some pitfall in the Velocity Verlet algorithm when it comes to n>=2 and I've fallen into it.
There is implementation error somewhere in the above code and I don't see it.
Errors due to floating point number calculations accumulate due to small movements
of the large body. (Likely not the case see edit below.)
During attempts to debug this I've come across Energy drift in molecular dynamics simulation. Maybe this is what is happening here?
It doesn't seem like the orbit is falling apart but it is not the result that I expected
and I want to know why.
Can someone help me solve this mystery?
Edit:
I have tested double precision and only change is that now the energy of the smallest orbiting mass is much more stable.
Now increasing trend can be seen even for the smallest mass.
This hints that it is not a problem with precision of calculations.
I found out what was wrong.
What turned out to be a problem was updating the position of bodies one by one. Computation of acceleration assumes that no body was moved between the timesteps
however updating one by one resulted in some bodies having position from t and some from t + dt.
That difference in this specific system caused vectorial difference of the orbiting body speed to not be ideally pointing towards the center of mass.
In effect a small tangential component was generated and energy was being added to the system. The error was small but over time it accumulated and was visible.
I fixed the problem by performing each stage of verlet algorithm on all bodies at once. Here is revised code for the integrator:
for(std::size_t i=0; i<get_size(); i++)
{
position(i, 0) += velocity(i, 0)*sim_config.timestep + acceleration(i, 0)*sim_config.timestep*sim_config.timestep*static_cast<Real>(0.5);
position(i, 1) += velocity(i, 1)*sim_config.timestep + acceleration(i, 1)*sim_config.timestep*sim_config.timestep*static_cast<Real>(0.5);
position(i, 2) += velocity(i, 2)*sim_config.timestep + acceleration(i, 2)*sim_config.timestep*sim_config.timestep*static_cast<Real>(0.5);
}
for(std::size_t i=0; i<get_size(); i++)
{
velocity(i, 0) += acceleration(i, 0)*sim_config.timestep*static_cast<Real>(0.5);
velocity(i, 1) += acceleration(i, 1)*sim_config.timestep*static_cast<Real>(0.5);
velocity(i, 2) += acceleration(i, 2)*sim_config.timestep*static_cast<Real>(0.5);
}
for(std::size_t i=0; i<get_size(); i++)
{
const Vector3D<Real> forces = compute_net_grawitational(i);
acceleration(i, 0) = forces.x/mass(i);
acceleration(i, 1) = forces.y/mass(i);
acceleration(i, 2) = forces.z/mass(i);
}
for(std::size_t i=0; i<get_size(); i++)
{
velocity(i, 0) += acceleration(i, 0)*sim_config.timestep*static_cast<Real>(0.5);
velocity(i, 1) += acceleration(i, 1)*sim_config.timestep*static_cast<Real>(0.5);
velocity(i, 2) += acceleration(i, 2)*sim_config.timestep*static_cast<Real>(0.5);
Now the energy is not increasing even for the heaviest orbiting body:
The energy is still drifting however over time the differences seem to average out and changes are small relative to the total value. Plot is auto ranged so changes seem large but they are within +- 1% of total energy which is acceptable for my application.
Before OP found its answer I was working on reproducing the problem, out of curiosity; I was starting the debug phase (a quick peek to the first data reveals the presence of at least one major bug), but I think I'll drop the task (holidays are almost finished). Before deleting the code from my hard disk I thought to put it here anyway, for posterity.
#include <cmath>
#include <limits>
#include <iostream>
#include <fstream>
#include <string>
#include <exception>
#include <vector>
//----------------------------------------------------------------------
// Some floating point facilities
//----------------------------------------------------------------------
// Some floating point facilities
constexpr inline bool is_zero(double x) noexcept
{
return std::fabs(x) < std::numeric_limits<double>::epsilon();
}
constexpr inline double ratio(const double n, const double d) noexcept
{
return is_zero(d) ? n/std::numeric_limits<double>::epsilon() : n/d;
}
////////////////////////////////////////////////////////////////////////
struct Vector3D ////////////////////////////////////////////////////////
{
double x, y, z;
Vector3D() noexcept : x(0.0), y(0.0), z(0.0) {}
Vector3D(const double X, const double Y, const double Z) noexcept
: x(X), y(Y), z(Z) {}
Vector3D operator+=(const Vector3D& other) noexcept
{
x+=other.x; y+=other.y; z+=other.z;
return *this;
}
Vector3D operator-() const noexcept
{
return Vector3D{-x, -y, -z};
}
friend Vector3D operator+(const Vector3D& v1, const Vector3D& v2) noexcept
{
return Vector3D{v1.x+v2.x, v1.y+v2.y, v1.z+v2.z};
}
friend Vector3D operator-(const Vector3D& v1, const Vector3D& v2) noexcept
{
return Vector3D{v1.x-v2.x, v1.y-v2.y, v1.z-v2.z};
}
friend Vector3D operator*(double k, const Vector3D& v) noexcept
{
return Vector3D{k*v.x, k*v.y, k*v.z};
}
friend Vector3D operator/(const Vector3D& v, double k) noexcept
{
return Vector3D{v.x/k, v.y/k, v.z/k};
}
friend std::ostream& operator<<(std::ostream& os, const Vector3D& v)
{
os << v.x << ',' << v.y << ',' << v.z;
return os;
}
double norm2() const noexcept { return x*x + y*y + z*z; }
double norm() const noexcept { return std::sqrt(norm2()); }
};
////////////////////////////////////////////////////////////////////////
class Body /////////////////////////////////////////////////////////////
{
public:
Body(const double m, const Vector3D& pos, const Vector3D& spd) noexcept
: i_mass(m), i_pos(pos), i_spd(spd) {}
double mass() const noexcept { return i_mass; }
const Vector3D& position() const noexcept { return i_pos; }
const Vector3D& speed() const noexcept { return i_spd; }
const Vector3D& acceleration() const noexcept { return i_acc; }
Vector3D distance_from(const Body& other) const noexcept
{
return position() - other.position();
}
double kinetic_energy() const noexcept
{// ½ m·V²
return 0.5 * i_mass * i_spd.norm2();
}
Vector3D gravitational_force_on(const Body& other, const double G) const noexcept
{// G · M·m / d²
Vector3D disp = distance_from(other);
double d = disp.norm();
return ratio(G * mass() * other.mass(), d*d*d) * disp;
}
double gravitational_energy_with(const Body& other, const double G) const noexcept
{// U = -G · mi·mj / d
double d = distance_from(other).norm();
return ratio(G * mass() * other.mass(), d);
}
void apply_force(const Vector3D& f)
{// Newton's law: F=ma
i_acc = f / i_mass;
}
void evolve_speed(const double dt) noexcept
{
i_spd += dt * i_acc;
}
void evolve_position(const double dt) noexcept
{
i_pos += dt * i_spd;
}
private:
double i_mass;
Vector3D i_pos, // Position [<space>]
i_spd, // Speed [<space>/<time>]
i_acc; // Acceleration [<space>/<time>²]
};
////////////////////////////////////////////////////////////////////////
class Universe /////////////////////////////////////////////////////////
{
public:
Universe(const double g) noexcept : G(g) {}
void evolve(const double dt) noexcept
{
for(Body& body : i_bodies)
{
body.evolve_speed(dt/2);
body.evolve_position(dt);
}
for( auto ibody=i_bodies.begin(); ibody!=i_bodies.end(); ++ibody )
{
Vector3D f = gravitational_force_on_body(ibody);
ibody->apply_force(f);
ibody->evolve_speed(dt/2);
}
}
double kinetic_energy() const noexcept
{// K = ∑ ½ m·V²
double Ek = 0.0;
for( const Body& body : i_bodies ) Ek += body.kinetic_energy();
return Ek;
}
double gravitational_energy() const noexcept
{// U = ½ ∑ -G · mi·mj / d
double Eu = 0.0;
for( auto ibody=i_bodies.begin(); ibody!=i_bodies.end(); ++ibody )
{// Iterate over all the other bodies
for( auto ibody2=i_bodies.begin(); ibody2!=ibody; ++ibody2 )
Eu += ibody->gravitational_energy_with(*ibody2,G);
for( auto ibody2=ibody+1; ibody2!=i_bodies.end(); ++ibody2 )
Eu += ibody->gravitational_energy_with(*ibody2,G);
}
return Eu/2;
}
double total_energy() const noexcept { return kinetic_energy() + gravitational_energy(); }
Vector3D center_of_mass() const noexcept
{// U = ∑ m·vpos / M
Vector3D c;
double total_mass = 0.0;
for( const Body& body : i_bodies )
{
c += body.mass() * body.position();
total_mass += body.mass();
}
return c/total_mass;
}
Vector3D gravitational_force_on_body( std::vector<Body>::const_iterator ibody ) const noexcept
{// F = ∑ G · m·mi / di²
Vector3D f;
// Iterate over all the other bodies
for( auto ibody2=i_bodies.begin(); ibody2!=ibody; ++ibody2 )
f += ibody2->gravitational_force_on(*ibody,G);
for( auto ibody2=ibody+1; ibody2!=i_bodies.end(); ++ibody2 )
f += ibody2->gravitational_force_on(*ibody,G);
return f;
}
void add_body(const double m, const Vector3D& pos, const Vector3D& spd)
{
i_bodies.emplace_back(m,pos,spd);
}
const std::vector<Body>& bodies() const noexcept { return i_bodies; }
const double G; // Gravitational constant
private:
std::vector<Body> i_bodies;
};
////////////////////////////////////////////////////////////////////////
class Simulation ///////////////////////////////////////////////////////
{
public:
class Data /////////////////////////////////////////////////////////
{
public:
struct Sample ///////////////////////////////////////////////////
{
double time;
std::vector<Body> bodies; // Bodies status
Vector3D Cm; // Center of mass
double Ek, // Kinetic energy
Eu; // Potential energy
Sample(const double t,
const std::vector<Body>& bd,
const Vector3D& cm,
const double ek,
const double eu) : time(t),
bodies(bd),
Cm(cm),
Ek(ek),
Eu(eu) {}
};
void init(const std::vector<std::string>::size_type N) noexcept
{
i_samples.clear();
i_samples.reserve(N);
}
void add(Sample&& s)
{
i_samples.push_back(s);
}
void save_to_file(std::string fpath) const
{
std::ofstream f (fpath, std::ios::out);
if(!f.is_open()) throw std::runtime_error("Unable to open file " + fpath);
//f << "time,total-energy\n";
//for(const Sample& s : i_samples)
// f << s.time << ',' << (s.Ek+s.Eu) << '\n';
f << "time,bodies-xyz-pos\n";
for(const Sample& s : i_samples)
{
f << s.time;
for(const Body& body : s.bodies)
f << ',' << body.position();
f << '\n';
}
}
const std::vector<Sample>& samples() const noexcept { return i_samples; }
private:
std::vector<Sample> i_samples;
};
// Total time Time increment
void execute(Universe& universe, const double T, const double dt)
{
auto N = static_cast<std::size_t>(T/dt + 1);
i_data.init(N);
double t = 0.0;
do {
universe.evolve(dt);
i_data.add( Data::Sample(t, universe.bodies(),
universe.center_of_mass(),
universe.kinetic_energy(),
universe.gravitational_energy() ) );
t += dt;
}
while(t<T);
}
const Data& data() const noexcept { return i_data; }
private:
Data i_data;
};
//----------------------------------------------------------------------
int main()
{
// Creating a universe with a certain gravitational constant
Universe universe(1); // Our universe: 6.67408E-11 m³/kg s²
// Adding bodies (mass, initial position and speed)
universe.add_body(1000, Vector3D(0,0,0), Vector3D(0,0,0));
universe.add_body(100, Vector3D(10,0,0), Vector3D(0,10,0));
// Simulation settings
Simulation sim;
const double T = 100; // Total time
const double dt = 0.001; // Iteration time
std::cout << "Simulating T=" << T << " dt=" << dt << "...";
sim.execute(universe, T, dt);
std::cout << "...Done";
// Now do something with the simulation data...
// ...Edit as desired
//sim.data().save_to_file("data.txt");
{// Energies
std::string fname = "energies.txt";
std::ofstream f (fname, std::ios::out);
if(!f.is_open()) throw std::runtime_error("Unable to open file " + fname);
f << "time,kinetic,potential,total\n";
for(const Simulation::Data::Sample& s : sim.data().samples())
f << s.time << ',' << s.Ek << ',' << s.Eu << ',' << (s.Ek+s.Eu) << '\n';
}
{// Positions of...
std::size_t idx = 1; // ...Second body
std::string fname = "body" + std::to_string(idx) + ".txt";
std::ofstream f (fname, std::ios::out);
if(!f.is_open()) throw std::runtime_error("Unable to open file " + fname);
f << "time,body" << idx << ".x,body" << idx << ".y,body" << idx << ".z\n";
for(const Simulation::Data::Sample& s : sim.data().samples())
f << s.time << ',' << (s.bodies.begin()+idx)->position() << '\n';
}
}
I have an app based on qt (qcustomplot) that prints two different graphs. They have one point of intersection. How to find x and y coordinates of this point?
This doesn't have much to do with plotting, since you'd be investigating the underlying data. Let's say that we can interpolate between data points using lines, and the data sets are single-valued (i.e. for any x or key coordinate, there's only one value).
Online demo of the code below
Let's sketch a solution. First, some preliminaries, and we detect whether QCustomPlot was included so that the code can be tested without it - the necessary classes are mocked:
#define _USE_MATH_DEFINES
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <optional>
#include <type_traits>
#include <vector>
//#include "qcustomplot.h"
constexpr bool debugOutput = false;
#ifndef QCP_PLOTTABLE_GRAPH_H
struct QCPGraphData {
double key, value;
QCPGraphData() = default;
QCPGraphData(double x, double y) : key(x), value(y) {}
};
#endif
auto keyLess(const QCPGraphData &l, const QCPGraphData &r) { return l.key < r.key; }
#ifndef QCP_PLOTTABLE_GRAPH_H
template <typename T> struct QCPDataContainer : public std::vector<T> {
using std::vector<T>::vector;
void sort() { std::sort(this->begin(), this->end(), keyLess); }
};
using QCPGraphDataContainer = QCPDataContainer<QCPGraphData>;
#endif
using Point = QCPGraphData;
using Container = QCPGraphDataContainer;
static_assert(std::is_copy_constructible_v<Point>, "Point must be copy-constructible");
Some helper functions:
std::ostream &operator<<(std::ostream &os, const Point &p) {
return os << "(" << p.key << ", " << p.value << ")";
}
template <class T> bool has_unique_keys(const T &v) {
constexpr auto keyEqual = [](const Point &l, const Point &r) { return l.key == r.key; };
return std::adjacent_find(std::begin(v), std::end(v), keyEqual) == std::end(v);
}
template <class T> bool has_valid_points(const T& v) {
constexpr auto isValid = [](const Point &p) { return std::isfinite(p.key) && std::isfinite(p.value); };
return std::all_of(std::begin(v), std::end(v), isValid);
}
The line segment intersection finder:
// intersection of two line segments
std::optional<Point> intersection(const Point &a1, const Point &a2, const Point &b1, const Point &b2)
{
auto p1 = a1, p2 = a2, p3 = b1, p4 = b2;
assert(p1.key <= p2.key);
assert(p3.key <= p4.key);
if (debugOutput) std::cout << p1 << "-" << p2 << ", " << p3 << "-" << p4;
auto const denom = (p1.key - p2.key)*(p3.value - p4.value)
- (p1.value - p2.value)*(p3.key - p4.key);
if (fabs(denom) > 1e-6*(p2.key - p1.key)) {
// the lines are not parallel
auto const scale = 1.0/denom;
auto const q = p1.key*p2.value - p1.value*p2.key;
auto const r = p3.key*p4.value - p3.value*p4.key;
auto const x = (q*(p3.key-p4.key) - (p1.key-p2.key)*r) * scale;
if (debugOutput) std::cout << " x=" << x << "\n";
if (p1.key <= x && x <= p2.key && p3.key <= x && x <= p4.key) {
auto const y = (q*(p3.value-p4.value) - (p1.value-p2.value)*r) * scale;
return std::optional<Point>(std::in_place, x, y);
}
}
else if (debugOutput) std::cout << "\n";
return std::nullopt;
}
An algorithm that walks down two lists of points sorted in ascending key (x) order, and finds all intersections of line segments spanning consecutive point pairs from these two lists:
std::vector<Point> findIntersections(const Container &a_, const Container &b_)
{
if (a_.size() < 2 || b_.size() < 2) return {};
static constexpr auto check = [](const auto &c){
assert(has_valid_points(c));
assert(std::is_sorted(c.begin(), c.end(), keyLess));
assert(has_unique_keys(c));
};
check(a_);
check(b_);
bool aFirst = a_.front().key <= b_.front().key;
const auto &a = aFirst ? a_ : b_, &b = aFirst ? b_ : a_;
assert(a.front().key <= b.front().key);
if (a.back().key < b.front().key) return {}; // the key spans don't overlap
std::vector<Point> intersections;
auto ia = a.begin(), ib = b.begin();
Point a1 = *ia++, b1 = *ib++;
while (ia->key < b1.key) a1=*ia++; // advance a until the key spans overlap
for (Point a2 = *ia, b2 = *ib;;) {
auto const ipt = intersection(a1, a2, b1, b2);
if (ipt)
intersections.push_back(*ipt);
bool advanceA = a2.key <= b2.key, advanceB = b2.key <= a2.key;
if (advanceA) {
if (++ia == a.end()) break;
a1 = a2, a2 = *ia;
}
if (advanceB) {
if (++ib == b.end()) break;
b1 = b2, b2 = *ib;
}
}
return intersections;
}
And a more generic version that can also sort the points in ascending key order:
auto findIntersections(Container &d1, Container &d2, bool presorted)
{
if (!presorted) {
d1.sort();
d2.sort();
}
return findIntersections(d1, d2);
}
And now some simple demonstration:
template <typename Fun>
Container makeGraph(double start, double step, double end, Fun &&fun) {
Container result;
int i = 0;
for (auto x = start; x <= end; x = ++i * step)
result.emplace_back(x, fun(x));
return result;
}
int main()
{
for (auto step2: {0.1, 0.1151484584}) {
auto sinPlot = makeGraph(-2*M_PI, 0.1, 3*M_PI, sin);
auto cosPlot = makeGraph(0., step2, 2*M_PI, cos);
auto intersections = findIntersections(sinPlot, cosPlot);
std::cout << "Intersections:\n";
for (auto &ip : intersections)
std::cout << " at " << ip << "\n";
}
}
Demo output:
Intersections:
at (0.785613, 0.706509)
at (3.92674, -0.706604)
Intersections:
at (0.785431, 0.706378)
at (3.92693, -0.706732)
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.
I am trying to use ceres solver to optimize the point cloud transformation process.
Through following the samples from ceres solver tutorial, I got a simple working version of the optimization process. However, when I try to further modify the function in operator (in MyCostFunctor class), the results are totally wrong (the solver converges, but gives wrong results). I found the problem is caused by the two lines of codes where I was trying to convert the parameters from template type T to Eigen matrix type.
Here are the codes:
template<typename T> inline
void DataTransfer(const T* input, Eigen::Matrix<T, Eigen::Dynamic, 1>& output) {
for (int i = 0; i < 12; ++i) {
output[i] = input[i];
}
}
template<typename T, typename PtT> inline
T* GetCorrespondingPoint(const T* rot, const PtT pt) {
//**!!!!!!!!!!! Error !!!!!!!!!!!**
//Eigen::Matrix<T, Eigen::Dynamic, 1> param_vecs = Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(12);
//DataTransfer<T>(rot, param_vecs);
// **!!!!!!!!!! Error !!!!!!!!!!!**
T result[3];
result[0] = rot[0] * T(pt(0)) + rot[1] * T(pt(1)) + rot[2] * T(pt(2)) + rot[9];
result[1] = rot[3] * T(pt(0)) + rot[4] * T(pt(1)) + rot[5] * T(pt(2)) + rot[10];
result[2] = rot[6] * T(pt(0)) + rot[7] * T(pt(1)) + rot[8] * T(pt(2)) + rot[11];
return result;
}
// A cost functor that implements the residual r = x - y.
// where x = R*x' + T or add more operations such as x = C*inverse((R*x')*A + T*B), A, B, C are related vectors or matrices
template<typename PtT>
class MyCostFunctor {
public:
MyCostFunctor(PtT& x, PtT& y, int pt_id)
:x_(x), y_(y), idx_(pt_id) {
}
template<typename T>
bool operator()(const T* const params, T* residual) const {
// Data transformation
T* rslt;
rslt = GetCorrespondingPoint<T, PtT>(params, x_);
residual[0] = T(rslt[0] - y_(0));
residual[1] = T(rslt[1] - y_(1));
residual[2] = T(rslt[2] - y_(2));
return true;
}
private:
PtT x_; // source point
PtT y_; // target point
int idx_; // source point idx
};
The two lines of codes are commented out in function "GetCorrespondingPoint".
The code of the main function are as follows:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <Eigen/Dense>
#include "ceres/ceres.h"
#include "glog/logging.h"
#include "ceres/dynamic_autodiff_cost_function.h"
using ceres::NumericDiffCostFunction;
using ceres::AutoDiffCostFunction;
using ceres::SizedCostFunction;
using ceres::CENTRAL;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve;
int main(int argc, char** argv){
google::InitGoogleLogging(argv[0]);
// 1. Sample Data Set Up
std::vector<Eigen::Vector3d> model_pts;
model_pts.clear();
std::vector<Eigen::Vector3d> target_pts;
target_pts.clear();
model_pts.push_back(Eigen::Vector3d(10.0, 10.0, 10.0));
model_pts.push_back(Eigen::Vector3d(20.0, 10.0, 10.0));
model_pts.push_back(Eigen::Vector3d(10.0, 20.0, 10.0));
model_pts.push_back(Eigen::Vector3d(10.0, 10.0, 20.0));
target_pts.push_back(Eigen::Vector3d(40.0, 40.0, 40.0));
target_pts.push_back(Eigen::Vector3d(40.0, 30.0, 40.0));
target_pts.push_back(Eigen::Vector3d(30.0, 40.0, 40.0));
target_pts.push_back(Eigen::Vector3d(40.0, 40.0, 30.0));
/// Set up the index for pairing the model and target points
std::vector<int> pt_idx;
pt_idx.push_back(0);
pt_idx.push_back(1);
pt_idx.push_back(2);
pt_idx.push_back(3);
// print pts
std::cout << "Model pts\t\tTarget pts\n";
for (int i = 0; i < model_pts.size(); ++i) {
std::cout << model_pts[i](0) << " " << model_pts[i](1) << " " << model_pts[i](2) << "\t\t\t"
<< target_pts[i](0) << " " << target_pts[i](1) << " " << target_pts[i](2) << "\n";
}
// Parameter Set up
double params[12];
for (int i = 0; i < 12; ++i) {
params[i] = 1.0;
}
// Set up the problem
int num_pts = target_pts.size();
Problem problem;
for (int i = 0; i < num_pts; ++i) {
problem.AddResidualBlock(
new AutoDiffCostFunction<MyCostFunctor<Eigen::Vector3d>, 3, 12>(new MyCostFunctor<Eigen::Vector3d>(model_pts[i], target_pts[i], pt_idx[i])), NULL,¶ms[0]);
}
// Set the solver options
ceres::Solver::Options options;
options.minimizer_progress_to_stdout = true;
// Run the solver!
ceres::Solver::Summary summary;
Solve(options, &problem, &summary);
std::cout << summary.FullReport() << "\n\n";
// print results
std::cout << "test results: \n";
for (int i = 0; i < model_pts.size(); ++i) {
Eigen::Vector3d pt;
pt(0) = params[0]*model_pts[i](0) + params[1]*model_pts[i](1) + params[2]*model_pts[i](2) + params[9];
pt(1) = params[3]*model_pts[i](0) + params[4]*model_pts[i](1) + params[5]*model_pts[i](2) + params[10];
pt(2) = params[6]*model_pts[i](0) + params[7]*model_pts[i](1) + params[8]*model_pts[i](2) + params[11];
std::cout << pt(0) << " " << pt(1) << " " << pt(2) << "\n";
}
return 0;
}
If I comment out the two lines, I will get right results:
results before data transfer
However, when I am trying to transfer the parameters into Eigen formate with those two lines of codes (NOT BEEN USED in the function, only copy and transfer), I will get the wrong results:
results after data transfer
Can anyone help me to figure out what's the problem and what should I do if I want to do some operation on the parameters to get the right corresponding points? Thanks!
Your code for the residual uses the matrix rot in row-major form, while Eigen defaults to column-major form:
https://eigen.tuxfamily.org/dox/group__TopicStorageOrders.html