14#include <gsl/gsl_eigen.h>
15#include <gsl/gsl_errno.h>
16#include <gsl/gsl_fft_halfcomplex.h>
17#include <gsl/gsl_fft_real.h>
30using namespace CurveFitting;
39double AbsValue(
double x) {
return fabs(
x); }
52 : m_tolerance(
std::max(
tolerance, g_tolerance)), m_n(
n), m_start(start), m_end(end) {
54 throw std::invalid_argument(
"Chebfun order must be greater than 0.");
58 m_bw.resize(
n + 1, 1.0);
59 for (
size_t i = 1; i <=
n; i += 2) {
85 if (p.size() !=
m_x.size()) {
86 throw std::invalid_argument(
"Function values have a wrong size in integration.");
91 std::vector<double>
tmp(p.size());
99 return std::accumulate(
tmp.begin(),
tmp.end(), 0.0);
107 throw std::logic_error(
"Cannot calculate x points of ChebfunBase: base is empty.");
109 if (
m_x.size() !=
m_n + 1) {
110 throw std::logic_error(
"X array has a wrong size.");
114 const double pin = M_PI / double(
m_n);
115 for (
size_t i = 0; i <=
m_n; ++i) {
117 m_x[i] = x0 + b * cos(
double(j) * pin);
128 std::vector<double> w(
n);
129 for (
size_t i = 0; i <
n; ++i) {
131 w[i] = 2.0 / (1.0 -
static_cast<double>(i * i));
138 for (
size_t i = 0; i <
n; ++i) {
140 for (
size_t j = 0; j <
n; ++j) {
141 b += w[j] * cos(M_PI *
double(i * j) /
double(
m_n));
144 if (i > 0 && i !=
m_n) {
165 for (
double coeff : a) {
176 if (shift > a.size() - 2)
178 for (
auto i = a.rbegin() + shift; i != a.rend() - 1; ++i) {
193 if (p.size() !=
m_x.size()) {
194 throw std::invalid_argument(
"Wrong array size in ChebdunBase::eval.");
196 if (x < m_start || x >
m_end)
198 auto ix = std::find(
m_x.begin(),
m_x.end(),
x);
199 if (ix !=
m_x.end()) {
200 auto i = std::distance(
m_x.begin(), ix);
205 auto xend =
m_x.end();
207 auto iw =
m_bw.begin();
208 for (ix =
m_x.begin(); ix != xend; ++ix, ++ip, ++iw) {
209 double w = *iw / (
x - *ix);
223 std::vector<double> &res)
const {
225 throw std::invalid_argument(
"Vector of x-values cannot be empty.");
228 res.resize(
x.size(), 0.0);
229 auto ix = std::lower_bound(
m_x.begin(),
m_x.end(),
x.front());
230 if (ix ==
m_x.end()) {
234 auto mXBegin =
m_x.begin();
235 auto mXEnd =
m_x.end();
236 auto pBegin = p.begin();
237 auto bwBegin =
m_bw.begin();
240 for (; i <
x.size(); ++i) {
245 for (; i <
x.size(); ++i) {
247 while (ix != mXEnd && xi > *ix)
253 auto j = std::distance(
m_x.begin(), ix);
260 for (
auto kx = mXBegin; kx != mXEnd; ++kx, ++kp, ++kw) {
261 double w = *kw / (xi - *kx);
265 res[i] =
value / weight;
277 std::vector<double> res;
289 using namespace std::placeholders;
290 if (a.size() !=
m_x.size()) {
291 throw std::invalid_argument(
"Cannot calculate derivative: coeffs vector has wrong size.");
295 aout[0] = 2.0 * a[1];
298 aout.resize(
m_n + 1);
300 aout[
m_n - 1] = 2.0 * double(
m_n) * a.back();
301 for (
size_t k =
m_n - 1; k > 1; --k) {
302 aout[k - 1] = aout[k + 1] + 2.0 * double(k) * a[k];
305 aout.front() = aout[2] / 2 + a[1];
309 using std::placeholders::_1;
310 std::transform(aout.begin(), aout.end(), aout.begin(),
311 std::bind(std::divides<double>(), _1, 0.5 * (
m_end -
m_start)));
322 using namespace std::placeholders;
323 if (a.size() !=
m_x.size()) {
324 throw std::invalid_argument(
"Cannot calculate integral: coeffs vector has wrong size.");
326 aout.resize(
m_n + 2);
328 for (
size_t k = 1; k <
m_n; ++k) {
329 aout[k] = (a[k - 1] - a[k + 1]) /
double(2 * k);
331 aout[
m_n] = a[
m_n - 1] / double(2 *
m_n);
332 aout[
m_n + 1] = a[
m_n] / double(2 * (
m_n + 1));
334 std::transform(aout.begin(), aout.end(), aout.begin(), std::bind(std::multiplies<double>(), _1,
d));
357template <
class FunctionType>
359 std::vector<double> &a,
double maxA,
double tolerance,
size_t maxSize) {
361 std::vector<double> p1, p2;
363 bool calcMaxA = maxA == 0.0;
368 size_t countNonZero = n0 / 2;
371 for (
size_t n = n0;
n < maxSize;
n *= 2) {
382 for (
double coeff : a) {
410 p = newBase->calcP(a);
413 p.assign(p2.begin(), p2.end());
417 size_t nNonZero = a.size();
418 for (
auto i = a.rbegin(); i != a.rend(); ++i) {
425 if (nNonZero == countNonZero) {
427 if (countNonZero < 2)
430 a.resize(countNonZero);
431 p = newBase->calcP(a);
434 countNonZero = nNonZero;
440 a.emplace_back(maxA);
446 std::vector<double> &a,
double maxA,
double tolerance,
size_t maxSize) {
452 std::vector<double> &a,
double maxA,
double tolerance,
size_t maxSize) {
453 return bestFitTempl<const API::IFunction &>(start, end, f, p, a, maxA,
tolerance, maxSize);
462 std::vector<double> space(
n);
464 const double dx =
width() / double(
n - 1);
465 for (
double &s : space) {
469 space.back() =
m_end;
479 const size_t nn =
m_n + 1;
481 if (p.size() != nn) {
482 throw std::invalid_argument(
"ChebfunBase: function vector must have same size as the base.");
485 std::vector<double> a(nn);
509 std::vector<double>
tmp(
m_n * 2);
510 std::reverse_copy(p.begin(), p.end(),
tmp.begin());
511 std::copy(p.begin() + 1, p.end() - 1,
tmp.begin() +
m_n + 1);
513 gsl_fft_real_workspace *
workspace = gsl_fft_real_workspace_alloc(2 *
m_n);
514 gsl_fft_real_wavetable *
wavetable = gsl_fft_real_wavetable_alloc(2 *
m_n);
520 for (
size_t i = 0; i < nn; ++i) {
521 a[i] = fc.
real(i) / double(
m_n);
539 if (
m_n + 1 != a.size()) {
540 std::stringstream mess;
541 mess <<
"chebfun: cannot calculate P from A - different sizes: " <<
m_n + 1 <<
" != " << a.size();
542 throw std::invalid_argument(mess.str());
544 std::vector<double> p(
m_n + 1);
548 std::vector<double>
tmp(
m_n * 2);
550 for (
size_t i = 0; i < nn; ++i) {
552 if (i == 0 || i == nn - 1)
556 gsl_fft_real_workspace *
workspace = gsl_fft_real_workspace_alloc(2 *
m_n);
557 gsl_fft_halfcomplex_wavetable *
wavetable = gsl_fft_halfcomplex_wavetable_alloc(2 *
m_n);
561 gsl_fft_halfcomplex_wavetable_free(
wavetable);
564 std::reverse_copy(
tmp.begin(),
tmp.begin() + nn, p.begin());
577 std::vector<double> res(
size());
578 std::transform(
m_x.begin(),
m_x.end(), res.begin(), std::move(f));
602 assert(
size() == p.size() * 2 - 1);
603 assert(
size() % 2 == 1);
605 std::vector<double> res(xp.size());
606 auto it2 = res.begin();
607 auto it1 = p.begin();
609 for (
auto x = xp.begin() + 1;
x != xp.end();
x += 2, ++it1, ++it2) {
614 *(res.end() - 1) = p.back();
626 assert(
size() == pEven.size() * 2 - 1);
627 assert(
size() % 2 == 1);
628 std::vector<double> pOdd(
size() - pEven.size());
629 std::vector<double> xOdd;
630 xOdd.reserve(pOdd.size());
632 for (
auto x =
m_x.begin() + 1;
x !=
m_x.end();
x += 2) {
633 xOdd.emplace_back(*
x);
641 std::vector<double> res(
size());
642 for (
size_t i = 0; i < xOdd.size(); ++i) {
643 res[2 * i] = pEven[i];
644 res[2 * i + 1] = pOdd[i];
646 res.back() = pEven.back();
657 std::vector<double> r;
661 const double epsilon = std::numeric_limits<double>::epsilon() * 100;
662 while (N > 0 &&
fabs(a[N]) < epsilon) {
669 const size_t N2 = 2 * N;
672 const double an = a[N];
674 const size_t lasti = N2 - 1;
675 for (
size_t i = 0; i < N; ++i) {
677 C.
set(i, i - 1, 1.0);
679 C.
set(N + i, N + i - 1, 1.0);
680 C.
set(i, lasti, -a[N - i] / an);
681 double tmp = -a[i] / an;
687 gsl_vector_complex *
eval = gsl_vector_complex_alloc(N2);
688 auto workspace = gsl_eigen_nonsymm_alloc(N2);
698 for (
size_t i = 0; i < N2; ++i) {
699 auto val = gsl_vector_complex_get(
eval, i);
700 double re = GSL_REAL(val);
701 double im = GSL_IMAG(val);
702 double ab = re * re + im * im;
703 if (
fabs(ab - 1.0) > 1e-2) {
711 if (im + firstIm < 1e-10) {
712 double x =
startX() + (re + 1.0) / 2.0 * Dx;
718 gsl_vector_complex_free(
eval);
730std::vector<double>
ChebfunBase::smooth(
const std::vector<double> &xvalues,
const std::vector<double> &yvalues)
const {
731 if (xvalues.size() != yvalues.size())
732 throw std::invalid_argument(
"Cannot smooth: input vectors have different sizes.");
733 const size_t n =
size();
734 std::vector<double>
y(
n);
737 auto ix = xvalues.begin();
739 auto xend = xvalues.end();
740 for (
size_t i = 0; i <
n; ++i) {
741 if (ix == xvalues.end()) {
745 auto ix0 = std::lower_bound(ix, xend,
x);
748 auto j = std::distance(xbegin, ix0);
750 y[i] = yvalues[j - 1] + (
x - xvalues[j - 1]) / (xvalues[j] - xvalues[j - 1]) * (yvalues[j] - yvalues[j - 1]);
757 const double guessSignalToNoiseRatio = 1e15;
760 std::vector<double> powerSpec(
n);
761 assert(powerSpec.size() ==
n);
764 std::transform(a.begin(), a.end(), powerSpec.begin(), AbsValue);
767 double noise = std::accumulate(powerSpec.begin() +
n / 2, powerSpec.end(), 0.0);
768 noise /=
static_cast<double>(
n / 2);
772 static_cast<size_t>(std::distance(powerSpec.begin(), std::max_element(powerSpec.begin(), powerSpec.end())));
775 noise = powerSpec[imax] / guessSignalToNoiseRatio;
783 std::vector<double> wf(
n);
795 for (
size_t i = 0; i <
n / 3; ++i) {
796 double av = (powerSpec[3 * i] + powerSpec[3 * i + 1] + powerSpec[3 * i + 2]) / 3;
809 for (
size_t i = 0; i < i0; ++i) {
810 double cd1 = powerSpec[i] / noise;
811 double cd2 = log(cd1);
812 wf[i] = cd1 / (1.0 + cd1);
813 auto j =
static_cast<double>(i + 1);
822 if (noise / powerSpec[imax] > 0.01 && i0 >
n / 2) {
828 auto ri0f =
static_cast<double>(i0 + 1);
829 double xm = (1.0 + ri0f) / 2;
831 double a1 = (xy - ri0f * xm * ym) / (xx - ri0f * xm * xm);
832 double b1 = ym - a1 * xm;
839 double c0, c1, c2, c3;
841 auto x0 = double(i0 + 1);
842 auto x1 = double(
n + 1);
846 double m0 = a1 * x0 + b1;
849 c2 = (m1 - m0 - a1 * (x1 - x0)) / ((x1 * x1 - x0 * x0) - 2 * x0 * (x1 - x0));
850 c1 = a1 - 2 * c2 * x0;
851 c0 = m0 - c2 * x0 * x0 - c1 * x0;
855 c1 = (m1 - m0) / (x1 - x0);
857 double tmp = exp(c1 *
double(i0 + 2) + c0);
861 double d0 = log(0.1 / 0.9);
862 c3 = (d0 * (x0 - x1) - 2 * (m0 - m1)) / (pow(x0, 3) - pow(x1, 3) - 3 * x0 * x1 * (x0 - x1));
863 c2 = (3 * c3 * (x0 * x0 - x1 * x1) - d0) / (2 * (x1 - x0));
864 c1 = -x1 * (3 * c3 * x1 + 2 * c2);
865 c0 = x1 * x1 * (2 * c3 * x1 + c2) + m1;
873 for (
size_t i = i0; i <
n; ++i) {
875 auto s = double(i + 1);
876 s = c0 + s * (c1 + s * (c2 + s * c3));
881 wf[i] = s / (1.0 + s);
886 std::transform(a.begin(), a.end(), wf.begin(), a.begin(), std::multiplies<double>());
gsl_fft_real_wavetable * wavetable
double value
The value of the point.
IPeaksWorkspace_sptr workspace
1D domain - a wrapper around an array of doubles.
A class to store values calculated by a function.
This is an interface to a fitting function - a semi-abstarct class.
virtual void function(const FunctionDomain &domain, FunctionValues &values) const =0
Evaluates the function for all arguments in the domain.
A wrapper around Eigen::Matrix.
void set(size_t i, size_t j, double value)
Set an element.
EigenMatrix tr() const
Calculate the eigensystem of a symmetric matrix.
map_type & mutator()
Get the map to Eigen matrix.
void zero()
Set all elements to zero.
The ChebfunBase class provides a base for function approximation with Chebyshev polynomials.
std::shared_ptr< ChebfunBase > integral(const std::vector< double > &a, std::vector< double > &aout) const
Calculate the integral.
void evalVector(const std::vector< double > &x, const std::vector< double > &p, std::vector< double > &res) const
Evaluate a function.
double eval(double x, const std::vector< double > &p) const
Evaluate a function.
size_t m_n
Polynomial order.
size_t size() const
Get the size of the base which is the number of x-points.
double m_start
Start of the interval.
void derivative(const std::vector< double > &a, std::vector< double > &aout) const
Calculate the derivative.
void calcIntegrationWeights() const
Calculate the integration weights.
double integrate(const std::vector< double > &p) const
Calculate an integral.
std::vector< double > m_x
The x-points.
std::vector< double > m_bw
The barycentric weights.
std::vector< double > roots(const std::vector< double > &a) const
Find all roots of a function on this interval.
static const size_t g_maxNumberPoints
Maximum number of (x) points in a base.
std::vector< double > linspace(size_t n) const
Create a vector of x values linearly spaced on the approximation interval.
static const double g_tolerance
Maximum tolerance in comparing doubles.
double width() const
Get the width of the interval.
const std::vector< double > & integrationWeights() const
Get a reference to the integration weights.
ChebfunBase(size_t n, double start, double end, double tolerance=0.0)
Constructor.
double endX() const
End of the interval.
void calcX()
Calculate the x-values based on the (start,end) interval.
static bool hasConverged(const std::vector< double > &a, double maxA, double tolerance, size_t shift=0)
Test an array of Chebyshev coefficients for convergence.
std::vector< double > m_integrationWeights
The integration weights.
double startX() const
Start of the interval.
static std::shared_ptr< ChebfunBase > bestFit(double start, double end, ChebfunFunctionType, std::vector< double > &p, std::vector< double > &a, double maxA=0.0, double tolerance=0.0, size_t maxSize=0)
Fit a function until full convergence.
const std::vector< double > & xPoints() const
Get a reference to the x-points.
std::vector< double > smooth(const std::vector< double > &xvalues, const std::vector< double > &yvalues) const
Smooth some data.
static std::shared_ptr< ChebfunBase > bestFitTempl(double start, double end, FunctionType f, std::vector< double > &p, std::vector< double > &a, double maxA, double tolerance, size_t maxSize)
Templated implementation of bestFit method.
double tolerance()
Tolerance for comparing doubles.
size_t order() const
Get the polynomial order of this base.
std::vector< double > fit(ChebfunFunctionType f) const
Calculate function values at chebfun x-points.
std::vector< double > calcP(const std::vector< double > &a) const
Calculate function values.
std::vector< double > calcA(const std::vector< double > &p) const
Calculate expansion coefficients.
double m_end
End of the interval.
std::vector< double > fitOdd(const ChebfunFunctionType &f, std::vector< double > &p) const
Calculate function values at odd-valued indices of the base x-points.
Class for helping to read the transformed data.
double real(size_t i) const
The real part of i-th transform coefficient.
void set(size_t i, const double &re, const double &im)
Set a new value for i-th complex coefficient.
std::shared_ptr< ChebfunBase > ChebfunBase_sptr
std::function< double(double)> ChebfunFunctionType
Type of the approximated function.
gsl_matrix_view getGSLMatrixView(map_type &tr)
take data from an Eigen Matrix and return a transposed a gsl view.