Given a set of 2d points (in Cartesian form), I need to find the minimum-area ellipse such that every point in the set lies either on or inside the ellipse.
I have found the solution in the form of pseudo-code on this site, but my attempt to implement the solution in C++ has been unsuccessful.
The following image illustrates graphically what the solution to my problem looks like:
In my attempt, I used the Eigen library for the various operations on matrices.
//The tolerance for error in fitting the ellipse
double tolerance = 0.2;
int n = 10; // number of points
int d = 2; // dimension
MatrixXd p = MatrixXd::Random(d,n); //Fill matrix with random points
MatrixXd q = p;
q.conservativeResize(p.rows() + 1, p.cols());
for(size_t i = 0; i < q.cols(); i++)
{
q(q.rows() - 1, i) = 1;
}
int count = 1;
double err = 1;
const double init_u = 1.0 / (double) n;
MatrixXd u = MatrixXd::Constant(n, 1, init_u);
while(err > tolerance)
{
MatrixXd Q_tr = q.transpose();
cout << "1 " << endl;
MatrixXd X = q * u.asDiagonal() * Q_tr;
cout << "1a " << endl;
MatrixXd M = (Q_tr * X.inverse() * q).asDiagonal();
cout << "1b " << endl;
int j_x, j_y;
double maximum = M.maxCoeff(&j_x, &j_y);
double step_size = (maximum - d - 1) / ((d + 1) * (maximum + 1));
MatrixXd new_u = (1 - step_size) * u;
new_u(j_x, 0) += step_size;
cout << "2 " << endl;
//Find err
MatrixXd u_diff = new_u - u;
for(size_t i = 0; i < u_diff.rows(); i++)
{
for(size_t j = 0; j < u_diff.cols(); j++)
u_diff(i, j) *= u_diff(i, j); // Square each element of the matrix
}
err = sqrt(u_diff.sum());
count++;
u = new_u;
}
cout << "3 " << endl;
MatrixXd U = u.asDiagonal();
MatrixXd A = (1.0 / (double) d) * (p * U * p.transpose() - (p * u) * (p * u).transpose()).inverse();
MatrixXd c = p * u;
The error occurs on the following line:
MatrixXd M = (Q_tr * X.inverse() * q).asDiagonal();
and it reads as follows:
run: /usr/include/eigen3/Eigen/src/Core/DenseBase.h:261: void Eigen::DenseBase<Derived>::resize(Eigen::Index, Eigen::Index) [with Derived = Eigen::Diagonal<Eigen::Matrix<double, -1, -1>, 0>; Eigen::Index = long int]: Assertion `rows == this->rows() && cols == this->cols() && "DenseBase::resize() does not actually allow to resize."' failed.
Aborted (core dumped)
Can someone please point out why this error is occurring or even better, give me some advice on how to fit an ellipse to a set of points using C++?
With Eigen, you can get the diagonal vector from a matrix with .diagonal(); you can treat a vector as a diagonal matrix with .asDiagonal(); but you cannot treat a dense matrix as a diagonal matrix. So that line should be
MatrixXd M = (Q_tr * X.inverse() * q).diagonal();
Related
I wrote the code in C++ which solves the time-dependent 1D Schrodinger equation for the anharmonic potential V = x^2/2 + lambda*x^4, using Thomas algorithm. My code is working and I animate the results in Mathematica, to check what is going on. I test the code against the known solution for the harmonic potential (I put lambda = 0), but the animation shows that abs(Psi) is changing with time, and I know that is not correct for the harmonic potential. Actually, I see that in one point it time it becomes constant, but before that is oscillating.
So I understand that I need to have constant magnitude of the wave function over the time interval, but I don't know how to do it, or where am I doing mistake.
Here is my code and the animation for 100 time steps and 100 points on the grid.
#include <iostream>
#include <iomanip>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <complex>
#include <fstream>
using namespace std;
// Mandatory parameters
const int L = 1; //length of domain in x direction
const int tmax = 10; //end time
const int nx = 100, nt = 100; //number of the grid points and time steps respectively
double lambda; //dictates the shape of the potential (we can use lambda = 0.0
// to test the code against the known solution for the harmonic
// oscillator)
complex<double> I(0.0, 1.0); //imaginary unit
// Derived parameters
double delta_x = 1. / (nx - 1);
//spacing between the grid points
double delta_t = 1. / (nt - 1);
//the time step
double r = delta_t / (delta_x * delta_x); //used to simplify expressions for
// the coefficients of the lhs and
// rhs of the matrix eqn
// Algorithm for solving the tridiagonal matrix system
vector<complex<double> > thomas_algorithm(vector<double>& a,
vector<complex<double> >& b,
vector<double>& c,
vector<complex<double> >& d)
{
// Temporary wave function
vector<complex<double> > y(nx + 1, 0.0);
// Modified matrix coefficients
vector<complex<double> > c_prime(nx + 1, 0.0);
vector<complex<double> > d_prime(nx + 1, 0.0);
// This updates the coefficients in the first row
c_prime[0] = c[0] / b[0];
d_prime[0] = d[0] / b[0];
// Create the c_prime and d_prime coefficients in the forward sweep
for (int i = 1; i < nx + 1; i++)
{
complex<double> m = 1.0 / (b[i] - a[i] * c_prime[i - 1]);
c_prime[i] = c[i] * m;
d_prime[i] = (d[i] - a[i] * d_prime[i - 1]) * m;
}
// This gives the value of the last equation in the system
y[nx] = d_prime[nx];
// This is the reverse sweep, used to update the solution vector
for (int i = nx - 1; i > 0; i--)
{
y[i] = d_prime[i] - c_prime[i] * y[i + 1];
}
return y;
}
void calc()
{
// First create the vectors to store the coefficients
vector<double> a(nx + 1, 1.0);
vector<complex<double> > b(nx + 1, 0.0);
vector<double> c(nx + 1, 1.0);
vector<complex<double> > d(nx + 1, 0.0);
vector<complex<double> > psi(nx + 1, 0.0);
vector<complex<double> > phi(nx + 1, 0.0);
vector<double> V(nx + 1, 0.0);
vector<double> x(nx + 1, 0);
vector<vector<complex<double> > > PSI(nt + 1,
vector<complex<double> >(nx + 1,
0.0));
vector<double> prob(nx + 1, 0);
// We don't have the first member of the left diagonal and the last member
// of the right diagonal
a[0] = 0.0;
c[nx] = 0.0;
for (int i = 0; i < nx + 1; i++)
{
x[i] = (-nx / 2) + i; // Values on the x axis
// Eigenfunction of the harmonic oscillator in the ground state
phi[i] = exp(-pow(x[i] * delta_x, 2) / 2) / (pow(M_PI, 0.25));
// Anharmonic potential
V[i] = pow(x[i] * delta_x, 2) / 2 + lambda * pow(x[i] * delta_x, 4);
// The main diagonal coefficients
b[i] = 2.0 * I / r - 2.0 + V[i] * delta_x * delta_x;
}
double sum0 = 0.0;
for (int i = 0; i < nx + 1; i++)
{
PSI[0][i] = phi[i]; // Initial condition for the wave function
sum0 += abs(pow(PSI[0][i], 2)); // Needed for the normalization
}
sum0 = sum0 * delta_x;
for (int i = 0; i < nx + 1; i++)
{
PSI[0][i] = PSI[0][i] / sqrt(sum0); // Normalization of the initial
// wave function
}
for (int j = 0; j < nt; j++)
{
PSI[j][0] = 0.0;
PSI[j][nx] = 0.0; // Boundary conditions for the wave function
d[0] = 0.0;
d[nx] = 0.0; // Boundary conditions for the rhs
// Fill in the current time step vector d representing the rhs
for (int i = 1; i < nx + 1; i++)
{
d[i] = PSI[j][i + 1]
+ (2.0 - 2.0 * I / r - V[i] * delta_x * delta_x) * PSI[j][i]
+ PSI[j][i - 1];
}
// Now solve the tridiagonal system
psi = thomas_algorithm(a, b, c, d);
for (int i = 1; i < nx; i++)
{
PSI[j + 1][i] = psi[i]; // Assign values to the wave function
}
for (int i = 0; i < nx + 1; i++)
{
// Probability density of the wave function in the next time step
prob[i] = abs(PSI[j + 1][i] * conj(PSI[j + 1][i]));
}
double sum = 0.0;
for (int i = 0; i < nx + 1; i++)
{
sum += prob[i] * delta_x;
}
for (int i = 0; i < nx + 1; i++)
{
// Normalization of the wave function in the next time step
PSI[j + 1][i] /= sqrt(sum);
}
}
// Opening files for writing the results
ofstream file_psi_re, file_psi_imag, file_psi_abs, file_potential,
file_phi0;
file_psi_re.open("psi_re.dat");
file_psi_imag.open("psi_imag.dat");
file_psi_abs.open("psi_abs.dat");
for (int i = 0; i < nx + 1; i++)
{
file_psi_re << fixed << x[i] << " ";
file_psi_imag << fixed << x[i] << " ";
file_psi_abs << fixed << x[i] << " ";
for (int j = 0; j < nt + 1; j++)
{
file_psi_re << fixed << setprecision(6) << PSI[j][i].real() << " ";
file_psi_imag << fixed << setprecision(6) << PSI[j][i].imag()
<< " ";
file_psi_abs << fixed << setprecision(6) << abs(PSI[j][i]) << " ";
}
file_psi_re << endl;
file_psi_imag << endl;
file_psi_abs << endl;
}
}
int main(int argc, char **argv)
{
calc();
return 0;
}
The black line is abs(psi), the red one is Im(psi) and the blue one is Re(psi).
(Bear in mind that my computational physics course was ten years ago now)
You say you are solving a time-dependent system, but I don't see any time-dependence (even if lambda != 0). In the Schrodinger Equation, if the potential function does not depend on time then the different equation is called separable because you can solve the time component and spatial component of the differential equation separately.
The general solution in that case is just the solution to the time-independent Schrodinger Equation multiplied by exp(-iE/h_bar). When you plot the magnitude of the probability that term just becomes 1 and so the probability doesn't change over time. In these cases people quite typically just ignore the time component altogether.
All this is to say that since your potential function doesn't depend on time then you aren't solving a time-dependent Schrodinger Equation. The Tridiagonal Matrix Algorithm can only be used to solve ordinary differential equations, whereas if your potential depended on time you would have a partial differential equation and would need a different method to solve it. Also as a result of that plotting the probability density over time is rarely interesting.
As for why your potential is not constant, numerical methods for finding eigenvalues and eigenvectors rarely produce the normalised eigenvectors naturally, so are you manually normalising your eigenvector before computing your probabilities?
I copied the code of Moro's Inverse Cumulative function of Normal distribution, where I added an extra error handling to deal with the input that is out of range (0,1). When I tested the code with all valid inputs, the speed is slowed down about 25% (6ns vs 8ns). I'm quite curious that why even no execution the throw exception still slow down the performance?
Code of Inverse function:
double Inverse(double u)
{
double a[4] = {
2.50662823884,
-18.61500062529,
41.39119773534,
-25.44106049637 };
double b[4] = {
-8.47351093090,
23.08336743743,
-21.06224101826,
3.13082909833 };
double c[9] = {
0.3374754822726147,
0.9761690190917186,
0.1607979714918209,
0.0276438810333863,
0.0038405729373609,
0.0003951896511919,
0.0000321767881768,
0.0000002888167364,
0.0000003960315187 };
// Assert( 0 < u && u < 1 );
if (u >= 1 || u <= 0){
throw std::invalid_argument("Input out of range.");
}
/*return the inverse of cumulative normal distribution fonction*/
double x, r;
x = u - 0.5;
if (fabs(x)<0.42) {
r = x*x;
r = x*(((a[3] * r + a[2])*r + a[1])*r + a[0]) / ((((b[3] * r + b[2])*r + b[1])*r + b[0])*r + 1.0);
return(r);
}
r = u;
if (x>0.0) r = 1 - u;
r = log(-log(r));
r = c[0] + r*(c[1] + r*(c[2] + r*(c[3] + r*(c[4] + r*(c[5] + r*(c[6] + r*(c[7] + r*c[8])))))));
if (x<0.0) r = -r;
return(r);
}
And the test code is
int m = 1000000, n = 1000;
double z, p, dz = 1.0 / double(m) / double(n);
clock_t t1, t2;
t1 = clock();
z = 1e-9;
p = 0.0;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
z += dz * 0.999;
p = Inverse(z);
}
}
t2 = clock();
cout << z << '\t' << p << endl;
cout << "Inverse" << '\t' << float(t2 - t1) / CLOCKS_PER_SEC << endl;
Even if the exception is not executed, the computer still has to check the value of the variable 'u' in the condition, which takes time.
I'm having trouble compiling this program with #include. I see that if I comment out this line it compiles.
MatrixXd A = (1.0 / (double) d) * (p * U * p.transpose() - (p * u) * (p * u).transpose()).inverse();
I am unable to change the header since I need to run this code in ROS and I have to use the Eigen library built within. I am using the code as described in this link
How to fit a bounding ellipse around a set of 2D points.
Any help is greatly appricated.
pound include iostream
pound include Eigen/Dense
using namespace std;
using Eigen::MatrixXd;
int main ( )
{
//The tolerance for error in fitting the ellipse
double tolerance = 0.2;
int n = 12; // number of points
int d = 2; // dimension
MatrixXd p(d,n); //Fill matrix with random points
p(0,0) = -2.644722;
p(0,1) = -2.644961;
p(0,2) = -2.647504;
p(0,3) = -2.652942;
p(0,4) = -2.652745;
p(0,5) = -2.649508;
p(0,6) = -2.651345;
p(0,7) = -2.654530;
p(0,8) = -2.651370;
p(0,9) = -2.653966;
p(0,10) = -2.661322;
p(0,11) = -2.648208;
p(1,0) = 4.764553;
p(1,1) = 4.718605;
p(1,2) = 4.676985;
p(1,3) = 4.640509;
p(1,4) = 4.595640;
p(1,5) = 4.546657;
p(1,6) = 4.506177;
p(1,7) = 4.468277;
p(1,8) = 4.421263;
p(1,9) = 4.383508;
p(1,10) = 4.353276;
p(1,11) = 4.293307;
cout << p << endl;
MatrixXd q = p;
q.conservativeResize(p.rows() + 1, p.cols());
for(size_t i = 0; i < q.cols(); i++)
{
q(q.rows() - 1, i) = 1;
}
int count = 1;
double err = 1;
const double init_u = 1.0 / (double) n;
MatrixXd u = MatrixXd::Constant(n, 1, init_u);
while(err > tolerance)
{
MatrixXd Q_tr = q.transpose();
cout << "1 " << endl;
MatrixXd X = q * u.asDiagonal() * Q_tr;
cout << "1a " << endl;
MatrixXd M = (Q_tr * X.inverse() * q).diagonal();
cout << "1b " << endl;
int j_x, j_y;
double maximum = M.maxCoeff(&j_x, &j_y);
double step_size = (maximum - d - 1) / ((d + 1) * (maximum + 1));
MatrixXd new_u = (1 - step_size) * u;
new_u(j_x, 0) += step_size;
cout << "2 " << endl;
//Find err
MatrixXd u_diff = new_u - u;
for(size_t i = 0; i < u_diff.rows(); i++)
{
for(size_t j = 0; j < u_diff.cols(); j++)
u_diff(i, j) *= u_diff(i, j); // Square each element of the matrix
}
err = sqrt(u_diff.sum());
count++;
u = new_u;
}
cout << "3 " << endl;
MatrixXd U = u.asDiagonal();
MatrixXd A = (1.0 / (double) d) * (p * U * p.transpose() - (p * u) * (p * u).transpose()).inverse();
MatrixXd c = p * u;
cout << A << endl;
cout << c << endl;
return 0;
}
If I replace the obvious pound include bogus by
#include <iostream>
#include <Eigen/Dense>
it compiles just fine. It also runs, prints some numbers and returns 0.
I'm trying to get a hold on how to work with splines in Eigen, specifically I want do find the value of the spline interpolation and its first and second derivatives in some point. Finding the interpolated value is easy, but when I try to calculate the derivative I get strange values.
I tried following the instructions for the derivatives command in the manual (http://eigen.tuxfamily.org/dox/unsupported/classEigen_1_1Spline.html#af3586ab1929959e0161bfe7da40155c6), and this is my attempt in code:
#include <iostream>
#include <Eigen/Core>
#include <unsupported/Eigen/Splines>
using namespace Eigen;
using namespace std;
double scaling(double x, double min, double max) // for scaling numbers
{
return (x - min)/(max - min);
}
VectorXd scale(VectorXd xvals) // for scaling vectors
{
const double min = xvals.minCoeff();
const double max = xvals.maxCoeff();
for (int k = 0; k < xvals.size(); k++)
xvals(k) = scaling(xvals(k),min,max);
return xvals;
}
int main()
{
typedef Spline<double,1,3> spline;
VectorXd xvals = (VectorXd(4) << 0,1,2,4).finished();
VectorXd yvals = xvals.array().square(); // x^2
spline testspline = SplineFitting<spline>::Interpolate(yvals.transpose(), 3,
scale(xvals).transpose());
cout << "derivative at x = 0: " << testspline.derivatives(0.00,2) << endl;
cout << "derivative at x = 1: " << testspline.derivatives(0.25,2) << endl;
cout << "derivative at x = 2: " << testspline.derivatives(0.50,2) << endl;
cout << "derivative at x = 3: " << testspline.derivatives(0.75,2) << endl;
cout << "derivative at x = 4: " << testspline.derivatives(1.00,2) << endl;
}
it outputs
derivative at x = 0: 0 0 32
derivative at x = 1: 1 8 32
derivative at x = 2: 4 16 32
derivative at x = 3: 9 24 32
derivative at x = 4: 16 32 32
That is, the interpolation is correct (c.f. x = 3), but the derivatives are not, and they are off in a systematic way, so I'm thinking I'm doing something wrong. Since these follow x^2, the derivatives should be 0,2,4,6,8 and the second order derivative should be 2.
Any ideas on how to solve this?
Edit 1
Changing x^2 to x^2 + 1 yields the same derivatives, so that checks out at least. But changing x^2 to x^3 is wrong, but wrong in a slightly different way, output would then be:
derivative at x = 2: 8 48 192
derivative at x = 3: 27 108 288
derivative at x = 4: 64 192 384
Which is wrong, it should be 6, 9, 12.
Also running the x^2 case, but changing he input vector to 0,1,...9 yields the same derivative as using the original input vector, but the second order derivative becomes a steady 200, which too is wrong. I fail to see why the second order derivative should depend on the number of input points.
Solved it. You were very close. All you had to do was scale the derivatives
with
1 / (x_max - x_min) (first derivative)
1 / (x_max - x_min)^2 (second derivative).
TLDR: You normalized the x values to be between 0 and 1 while fitting the spline, but you didn't scale the y values.
Instead of the spline fitting x^2, you actually fitted:
x_norm = (x - x_min) / (x_max - x_min)
y = x_norm**2
So using the chain rule the first derivative of y = x_norm**2 would be 2x / (x_max - x_min) and the second derivative would be 2 / (x_max - x_min)**2.
Full example code:
#include <iostream>
#include <Eigen/Core>
#include <unsupported/Eigen/Splines>
using namespace Eigen;
using namespace std;
VectorXd normalize(const VectorXd &x) {
VectorXd x_norm;
x_norm.resize(x.size());
const double min = x.minCoeff();
const double max = x.maxCoeff();
for (int k = 0; k < x.size(); k++) {
x_norm(k) = (x(k) - min)/(max - min);
}
return x_norm;
}
int main() {
typedef Spline<double, 1, 3> Spline1D;
typedef SplineFitting<Spline1D> Spline1DFitting;
const Vector4d x{0, 1, 2, 4};
const Vector4d y = (x.array().square()); // x^2
const auto knots = normalize(x); // Normalize x to be between 0 and 1
const double scale = 1 / (x.maxCoeff() - x.minCoeff());
const double scale_sq = scale * scale;
Spline1D spline = Spline1DFitting::Interpolate(y.transpose(), 3, knots);
cout << "1st deriv at x = 0: " << spline.derivatives(0.00, 1)(1) * scale << endl;
cout << "1st deriv at x = 1: " << spline.derivatives(0.25, 1)(1) * scale << endl;
cout << "1st deriv at x = 2: " << spline.derivatives(0.50, 1)(1) * scale << endl;
cout << "1st deriv at x = 3: " << spline.derivatives(0.75, 1)(1) * scale << endl;
cout << "1st deriv at x = 4: " << spline.derivatives(1.00, 1)(1) * scale << endl;
cout << endl;
/**
* IMPORTANT NOTE: Eigen's spline module is not documented well. Once you fit a spline
* to find the derivative of the fitted spline at any point u [0, 1] you call:
*
* spline.derivatives(u, 1)(1)
* ^ ^ ^
* | | |
* | | +------- Access the result
* | +---------- Derivative order
* +------------- Parameter u [0, 1]
*
* The last bit `(1)` is if the spline is 1D. And value of `1` for the first
* order. `2` for the second order. Do not forget to scale the result.
*
* For higher dimensions, treat the return as a matrix and grab the 1st or
* 2nd column for the first and second derivative.
*/
cout << "2nd deriv at x = 0: " << spline.derivatives(0.00, 2)(2) * scale_sq << endl;
cout << "2nd deriv at x = 1: " << spline.derivatives(0.25, 2)(2) * scale_sq << endl;
cout << "2nd deriv at x = 2: " << spline.derivatives(0.50, 2)(2) * scale_sq << endl;
cout << "2nd deriv at x = 3: " << spline.derivatives(0.75, 2)(2) * scale_sq << endl;
cout << "2nd deriv at x = 4: " << spline.derivatives(1.00, 2)(2) * scale_sq << endl;
return 0;
}
Example output:
1st deriv at x = 0: 4.52754e-16
1st deriv at x = 1: 2
1st deriv at x = 2: 4
1st deriv at x = 3: 6
1st deriv at x = 4: 8
2nd deriv at x = 0: 2
2nd deriv at x = 1: 2
2nd deriv at x = 2: 2
2nd deriv at x = 3: 2
2nd deriv at x = 4: 2
Edit: see working .h-file for calculating B-splines of any order at the bottom.
Disclaimer: this is not an answer to my question as it is actually stated in the title, but rather a work-around with some comments.
After deliberations with user #Paul H. (see comments) I realized that my limited understanding of splines might have caused some confusion on my part. After some scrutiny of the Eigen documentation it seems plausible that the derivative() command does indeed works as intended, hence making my question badly phrased. The derivative() calculates the derivative of the spline rather than the derivative of the fitted function, as I intended it to. I have not figured out a way to get Eigen to output the function derivatives from the fit, and I don't think it is designed to to this. However, the derivatives can of course be readily calculated once the fitted points is obtained using some standard algorithm for calculating derivatives.
I wrote the following .h-file for calculating splines and their derivatives in the process which I thought worthwhile sharing. It's fairly well commented for convenience.
Note that this program uses 1-indexing rather than 0-indexing of the splines, hence for an e.g. quadratic B-spline order should be set to 4. This is a small quirk that is be easily fixed by changing the calculations to match wikipedia.
bsplines.h
//
// Header-file for calculating splines using standard libraries
//
// usage:
//
// x bsplines class constructs a set of splines up to some given order,
// on some given knot sequence, the splines are stored in a vector,
// such that splines[a][b] accesses the spline of order a and index b
// x get<some_member>() is an accessor that returns a pointer to some
// data member of the spline
// x calcsplines() calculates spline values as well as first and second
// order derivatives on some predefined grid
// x calcspline() returns the spline value as well as first and second
// derivatives in some point. This alborithm is slower than the grid
// one, due to unnecessary recalculations of intermediate results
// x writesplines() writes the splines and their derivatives to a file
// x for more details se the class declaration below
// TODO:
// x change to 0-indexation
// x introduce the possibility of calculating higher order derivatives
// recursively
//
// change log:
//
// 1.0 - initial release
// 1.1 - reworked grid such that the class now expects separate
// grid and knot files.
// - added the ability to calculate spline value in a point
// rather than calculate values on a grid
// - added a feature to change knots and grid
// 1.1.1 - reworked how returning single values works
// 1.1.2 - enabled swapping grid
//
// Note:
//
// This file uses 1-indexation rathar than 0-indexation, hence a qubic spline
// would be k = 4. Someone should eventually fix this as this is non-standard.
//
// Also, while only standard libraries are used here, you might want to check out
// some linear algebra package (e.g. Armadillo or Eigen) if you're going to use the
// splines in a context where you need linear algebraic operations.
//
// Originally developed by David Andersson
//
#include <iomanip>
#include <sstream>
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
#include <functional>
using namespace std;
typedef unsigned int uint;
class bsplines // class for bsplines
{
// private section
uint order; // order of spline
uint gridpts; // number of grid points
uint knotpts; // number of knot points
double tolerance; // tolerance for float comparisons
vector<double> knots; // knot sequence
vector<double> grid; // grid points
class spline // a member spline in the set of splines
{
int index; // the spline index, or number
vector<double> vals; // spline values
vector<double> d1; // spline first derivatives
vector<double> d2; // spline second derivatives
double tval; // same, but in one point
double td1;
double td2;
friend bsplines; // for ease of access
public:
};
vector<vector <spline>> splines; // the set of splines
// puclic section
public:
void readknots(string); // read knots from file
void readknotsnorm(string); // read knots from file and normalize
void readgrid(string); // read grid from file
void swapgrid(string); // reads and swaps new grid from file
void writesplines(); // write spline vals and derivs to file
void buildsplines(); // build the set of splines
void calcsplines(); // calculate spline vals and derivs
void printknots(); // print knot sequence
void printgrid(); // print grid
void printgridsize(); // print gridsize
void printvals(uint,uint); // print values of a spline
vector <double> calcspline(uint,uint,double); // calculate spline in point
// accessors // returns pointer to member
vector <double>* getknots(){return &knots;}
vector <double>* getgrid(){return &grid;}
uint* getknotpts(){return &knotpts;}
uint* getgridpts(){return &gridpts;}
uint getnosplines(uint m){return splines[m].size();}
vector <spline>* getsplines(uint m){return &splines[m];}
vector <double>* getvals(uint m, uint n){return &splines[m][n].vals;}
vector <double>* getd1(uint m, uint n){return &splines[m][n].d1;}
vector <double>* getd2(uint m, uint n){return &splines[m][n].d2;}
// constructor // sets up the spline class
bsplines (string iknots, string igrid, uint iorder, double itol)
:order(iorder), tolerance(itol)
{
readknots(iknots);
readgrid(igrid);
buildsplines();
}
};
void bsplines::buildsplines()
{
{
for (uint l = 1; l <= order; l++)
{
vector <spline> splinevec;
for (uint k = 0; k < knotpts - l; k++)
{
spline tmp;
tmp.index = k;
tmp.vals.reserve(gridpts);
tmp.d1.reserve(gridpts);
tmp.d2.reserve(gridpts);
splinevec.push_back(tmp);
}
splines.push_back(splinevec);
}
}
}
vector <double> bsplines::calcspline(uint m, uint n, double x)
{
// first order splines // exceptions handles infinities
for (auto& sp : splines[0])
{
uint i = sp.index;
if (x > knots[i+1])
sp.tval = 0;
else if ((x >= knots[i] && x < knots[i+1]) || x == knots.back())
sp.tval = 1;
else
sp.tval = 0;
}
// higher order splines
for (uint o = 1; o < order; o++)
{
uint oo = o+1; // compensating for 1-indexation
for (auto& sp : splines[o])
{
uint i = sp.index;
double t1 = knots[i+oo-1] - knots[i];
double t2 = knots[i+oo] - knots[i+1];
double c = 0;
if (abs(t1) > tolerance)
c += (x - knots[i]) / t1 * splines[o-1][i].tval;
if (abs(t2) > tolerance)
c += (knots[i+oo] - x) / t2 * splines[o-1][i+1].tval;
sp.tval = c;
}
}
uint o = order - 1;
// first order derivatives
for (auto& sp : splines[o])
{
uint i = sp.index;
double t1 = knots[i+order-1] - knots[i];
double t2 = knots[i+order] - knots[i+1];
double c = 0;
if (abs(t1) > tolerance)
c += 1.0 / t1 * splines[o-1][i].tval;
if (abs(t2) > tolerance)
c -= 1.0 / t2 * splines[o-1][i+1].tval;
c *= (order-1);
sp.td1 = c;
}
// second order derivatives
for (auto& sp : splines[o])
{
uint i = sp.index;
double t1 = (knots[i+order-1] - knots[i+0]) * (knots[i+order-2] - knots[i+0]);
double t2 = (knots[i+order-1] - knots[i+0]) * (knots[i+order-1] - knots[i+1]);
double t3 = (knots[i+order-0] - knots[i+1]) * (knots[i+order-1] - knots[i+1]);
double t4 = (knots[i+order-0] - knots[i+1]) * (knots[i+order-0] - knots[i+2]);
double c = 0;
if (abs(t1) > tolerance)
c += 1.0 / t1 * splines[o-2][sp.index].tval;
if (abs(t2) > tolerance)
c -= 1.0 / t2 * splines[o-2][sp.index+1].tval;
if (abs(t3) > tolerance)
c -= 1.0 / t3 * splines[o-2][sp.index+1].tval;
if (abs(t4) > tolerance)
c += 1.0 / t4 * splines[o-2][sp.index+2].tval;
c *= (order-1)*(order-2);
sp.td2 = c;
}
vector <double> retvals = {splines[m][n].tval, splines[m][n].td1, splines[m][n].td2};
return retvals;
}
void bsplines::calcsplines()
{
// first order splines
for (auto& sp : splines[0])
{
uint i = sp.index;
for (auto& x : grid)
{
if (x > knots[i+1])
sp.vals.push_back(0);
else if ((x >= knots[i] && x < knots[i+1]) || x == knots.back())
sp.vals.push_back(1);
else
sp.vals.push_back(0);
}
}
// higher order splines
for (uint o = 1; o < order; o++)
{
uint oo = o+1; // compensating for 1-indexation
for (auto& sp : splines[o])
{
uint i = sp.index;
double t1 = knots[i+oo-1] - knots[i];
double t2 = knots[i+oo] - knots[i+1];
for (auto& x : grid)
{
uint k = &x - &grid[0];
double c = 0;
if (abs(t1) > tolerance)
c += (x - knots[i]) / t1 * splines[o-1][i].vals[k];
if (abs(t2) > tolerance)
c += (knots[i+oo] - x) / t2 * splines[o-1][i+1].vals[k];
sp.vals.push_back(c);
}
}
}
uint o = order - 1; // use this one when accessing splines;
// first order derivatives
for (auto& sp : splines[o])
{
uint i = sp.index;
double t1 = knots[i+order-1] - knots[i];
double t2 = knots[i+order] - knots[i+1];
for (auto& x : grid)
{
uint k = &x - &grid[0];
double c = 0;
if (abs(t1) > tolerance)
c += 1.0 / t1 * splines[o-1][i].vals[k];
if (abs(t2) > tolerance)
c -= 1.0 / t2 * splines[o-1][i+1].vals[k];
c *= (order-1);
sp.d1.push_back(c);
}
}
// second order derivatives
for (auto& sp : splines[o])
{
uint i = sp.index;
double t1 = (knots[i+order-1] - knots[i+0]) * (knots[i+order-2] - knots[i+0]);
double t2 = (knots[i+order-1] - knots[i+0]) * (knots[i+order-1] - knots[i+1]);
double t3 = (knots[i+order-0] - knots[i+1]) * (knots[i+order-1] - knots[i+1]);
double t4 = (knots[i+order-0] - knots[i+1]) * (knots[i+order-0] - knots[i+2]);
for (auto& x : grid)
{
uint k = &x - &grid[0];
double c = 0;
if (abs(t1) > tolerance)
c += 1.0 / t1 * splines[o-2][sp.index].vals[k];
if (abs(t2) > tolerance)
c -= 1.0 / t2 * splines[o-2][sp.index+1].vals[k];
if (abs(t3) > tolerance)
c -= 1.0 / t3 * splines[o-2][sp.index+1].vals[k];
if (abs(t4) > tolerance)
c += 1.0 / t4 * splines[o-2][sp.index+2].vals[k];
c *= (order-1)*(order-2);
sp.d2.push_back(c);
}
}
}
void bsplines::readknots(string knotfile)
{
double x;
ifstream readknots(knotfile);
while (readknots >> x)
knots.push_back(x);
for (uint k = 0; k < order - 1; k++)
{
knots.insert(knots.begin(),knots.front());
knots.insert(knots.end(),knots.back());
}
knotpts = knots.size();
}
void bsplines::readknotsnorm(string knotfile)
{
double x;
knots.reserve(knotpts + 2*(order - 1));
ifstream readknots(knotfile);
while (readknots >> x)
knots.push_back(x);
auto minmax = minmax_element(begin(knots), end(knots));
double min = *(minmax.first);
double max = *(minmax.second);
for (auto& el : knots)
el = (el - min) / (max-min);
}
void bsplines::readgrid(string gridfile)
{
double x;
ifstream readgrid(gridfile);
while (readgrid >> x)
grid.push_back(x);
gridpts = grid.size();
}
void bsplines::swapgrid(string gridfile)
{
grid = {};
double x;
ifstream readgrid(gridfile);
while (readgrid >> x)
grid.push_back(x);
gridpts = grid.size();
}
void bsplines::printknots()
{
cout << "content in knot vector: " << endl;
for (auto& el : knots)
cout << el << " ";
cout << endl;
}
void bsplines::printgrid()
{
cout << "content in grid vector: " << endl;
for (auto& el : grid)
cout << el << " ";
cout << endl;
}
void bsplines::printgridsize()
{
cout << "number of grid points: " << endl << grid.size() << endl;
}
void bsplines::printvals(uint m, uint n)
{
cout << "content in spline (B" << m << "," << n << ") vals vector: " << endl;
for (auto& el : splines[n][m].vals)
cout << el << " ";
cout << endl;
}
void bsplines::writesplines()
{
for (uint o = 0; o < order; o++)
for (auto& sp : splines[o])
{
uint i = sp.index;
ostringstream namestream;
namestream << "B(" << fixed << setprecision(1) << i << ","
<< fixed << setprecision(1) << o << ").csv";
string filename = namestream.str();
ofstream fs;
fs.open(filename);
if (o < order - 1)
{
for (uint k = 0; k < sp.vals.size(); k++)
fs << sp.vals[k] << "," << 0 << "," << 0 << endl;
fs.close();
}
else
{
for (uint k = 0; k < sp.vals.size(); k++)
fs << sp.vals[k] << "," << sp.d1[k] << "," << sp.d2[k] << endl;
fs.close();
}
cout << "write " << sp.vals.size() << " numbers to " << filename << endl;
}
}
Edit: updated .h-file.
I need to loop over a given number of sampling points. Those sampling points are normalized vectors representing directions. They should be calculate them in code. Starting with a forward vector 1, 0, I want to rotate around the origin so that I came up with the given number of directions.
for(int i = 0; i < number_of_sampling_points; ++i)
{
// get direction vector based on i and number_of_sampling_points
// ...
}
For example, with number_of_sampling_points is 4 inside the loop I want to get the value pairs 1, 0, 0, 1, -1, 0, 0, -1. The order doesn't matter.
Try this:
const double PI = 3.14159265358979323846;
const int number_of_sampling_points = 4;
for (int i = 0; i < number_of_sampling_points; ++i)
{
const double a = PI * 2 * (1.0 - i) / number_of_sampling_points;
double x = sin(a);
double y = cos(a);
cout << "(" << x << " , " << y << ")" << endl;
}
Output (rounded):
(1 , 0)
(0 , 1)
(-1 , 0)
(0 , -1)
Use trig:
double x = std::cos(pi * 2 * (double)i / (double)number_of_sampling_points);
double y = std::sin(pi * 2 * (double)i / (double)number_of_sampling_points);