Mantid
Loading...
Searching...
No Matches
ChebfunBase.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
13
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>
18
19#include <algorithm>
20#include <cassert>
21#include <cmath>
22#include <functional>
23#include <limits>
24#include <numeric>
25#include <sstream>
26#include <utility>
27
29
30using namespace CurveFitting;
31
32// Set the comparison tolerance.
33const double ChebfunBase::g_tolerance = 1e-15;
34// Set the maximum number of points.
35const size_t ChebfunBase::g_maxNumberPoints = 1026;
36
37namespace {
38// Abs value function to be used with std::transform
39double AbsValue(double x) { return fabs(x); }
40} // namespace
41
51ChebfunBase::ChebfunBase(size_t n, double start, double end, double tolerance)
52 : m_tolerance(std::max(tolerance, g_tolerance)), m_n(n), m_start(start), m_end(end) {
53 if (n == 0) {
54 throw std::invalid_argument("Chebfun order must be greater than 0.");
55 }
56
57 m_x.resize(n + 1);
58 m_bw.resize(n + 1, 1.0);
59 for (size_t i = 1; i <= n; i += 2) {
60 m_bw[i - 1] = 1.0;
61 m_bw[i] = -1.0;
62 }
63 m_bw.front() /= 2.0;
64 m_bw.back() /= 2.0;
65 calcX();
66}
67
72const std::vector<double> &ChebfunBase::integrationWeights() const {
73 if (m_integrationWeights.size() != m_x.size()) {
75 }
77}
78
84double ChebfunBase::integrate(const std::vector<double> &p) const {
85 if (p.size() != m_x.size()) {
86 throw std::invalid_argument("Function values have a wrong size in integration.");
87 }
88 if (m_integrationWeights.empty()) {
90 }
91 std::vector<double> tmp(p.size());
92 std::transform(p.begin(), p.end(), m_integrationWeights.begin(), tmp.begin(), std::multiplies<double>());
93 // NB. for some reason the commented out expression gives more accurate result
94 // (when weights
95 // are not multiplied by the same factor) than the uncommented one. But moving
96 // the factor to the
97 // weights makes formulas involving weights simpler
98 // return std::accumulate(tmp.begin(),tmp.end(),0.0) * (m_end - m_start) / 2;
99 return std::accumulate(tmp.begin(), tmp.end(), 0.0);
100}
101
106 if (m_n == 0) {
107 throw std::logic_error("Cannot calculate x points of ChebfunBase: base is empty.");
108 }
109 if (m_x.size() != m_n + 1) {
110 throw std::logic_error("X array has a wrong size.");
111 }
112 const double x0 = (m_start + m_end) / 2;
113 const double b = (m_end - m_start) / 2;
114 const double pin = M_PI / double(m_n);
115 for (size_t i = 0; i <= m_n; ++i) {
116 size_t j = m_n - i;
117 m_x[i] = x0 + b * cos(double(j) * pin);
118 }
119}
120
125 size_t n = m_n + 1;
126 m_integrationWeights.resize(n);
127 // build an intermediate vector (these are different kind of weights)
128 std::vector<double> w(n);
129 for (size_t i = 0; i < n; ++i) {
130 if (i % 2 == 0) {
131 w[i] = 2.0 / (1.0 - static_cast<double>(i * i));
132 }
133 }
134 w[0] /= 2;
135 w[m_n] /= 2;
136 const double factor = (m_end - m_start) / 2;
137 // calculate the weights
138 for (size_t i = 0; i < n; ++i) {
139 double b = 0.0;
140 for (size_t j = 0; j < n; ++j) {
141 b += w[j] * cos(M_PI * double(i * j) / double(m_n));
142 }
143 b /= double(m_n);
144 if (i > 0 && i != m_n) {
145 b *= 2;
146 }
147 m_integrationWeights[i] = b * factor;
148 }
149}
150
161bool ChebfunBase::hasConverged(const std::vector<double> &a, double maxA, double tolerance, size_t shift) {
162 if (a.empty())
163 return true;
164 if (maxA == 0.0) {
165 for (double coeff : a) {
166 double tmp = fabs(coeff);
167 if (tmp > maxA) {
168 maxA = tmp;
169 }
170 }
171 }
172 if (maxA < tolerance || a.size() < 3) {
173 return true;
174 }
175
176 if (shift > a.size() - 2)
177 return true;
178 for (auto i = a.rbegin() + shift; i != a.rend() - 1; ++i) {
179 if (*i == 0.0)
180 continue;
181 return (fabs(*i) + fabs(*(i + 1))) / maxA / 2 < tolerance;
182 }
183 return false;
184}
185
192double ChebfunBase::eval(double x, const std::vector<double> &p) const {
193 if (p.size() != m_x.size()) {
194 throw std::invalid_argument("Wrong array size in ChebdunBase::eval.");
195 }
196 if (x < m_start || x > m_end)
197 return 0.0;
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);
201 return p[i];
202 }
203 double weight = 0.0;
204 double res = 0.0;
205 auto xend = m_x.end();
206 auto ip = p.begin();
207 auto iw = m_bw.begin();
208 for (ix = m_x.begin(); ix != xend; ++ix, ++ip, ++iw) {
209 double w = *iw / (x - *ix);
210 weight += w;
211 res += w * (*ip);
212 }
213 return res / weight;
214}
215
222void ChebfunBase::evalVector(const std::vector<double> &x, const std::vector<double> &p,
223 std::vector<double> &res) const {
224 if (x.empty()) {
225 throw std::invalid_argument("Vector of x-values cannot be empty.");
226 }
227
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()) {
231 return;
232 }
233
234 auto mXBegin = m_x.begin();
235 auto mXEnd = m_x.end();
236 auto pBegin = p.begin();
237 auto bwBegin = m_bw.begin();
238
239 size_t i = 0;
240 for (; i < x.size(); ++i) {
241 if (x[i] >= m_start)
242 break;
243 }
244
245 for (; i < x.size(); ++i) {
246 double xi = x[i];
247 while (ix != mXEnd && xi > *ix)
248 ++ix;
249 if (ix == mXEnd)
250 break;
251
252 if (xi == *ix) {
253 auto j = std::distance(m_x.begin(), ix);
254 res[i] = p[j];
255 } else {
256 double weight = 0.0;
257 double value = 0.0;
258 auto kp = pBegin;
259 auto kw = bwBegin;
260 for (auto kx = mXBegin; kx != mXEnd; ++kx, ++kp, ++kw) {
261 double w = *kw / (xi - *kx);
262 weight += w;
263 value += w * (*kp);
264 }
265 res[i] = value / weight;
266 }
267 }
268}
269
276std::vector<double> ChebfunBase::evalVector(const std::vector<double> &x, const std::vector<double> &p) const {
277 std::vector<double> res;
278 evalVector(x, p, res);
279 return res;
280}
281
287void ChebfunBase::derivative(const std::vector<double> &a, std::vector<double> &aout) const {
288
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.");
292 }
293 if (m_n == 0) {
294 aout.resize(2, 0.0);
295 aout[0] = 2.0 * a[1];
296 return;
297 }
298 aout.resize(m_n + 1);
299 aout.back() = 0.0;
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];
303 }
304 if (m_n > 2) {
305 aout.front() = aout[2] / 2 + a[1];
306 } else {
307 aout.front() = a[1];
308 }
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)));
312}
313
320ChebfunBase_sptr ChebfunBase::integral(const std::vector<double> &a, std::vector<double> &aout) const {
321
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.");
325 }
326 aout.resize(m_n + 2);
327 aout.front() = 0.0;
328 for (size_t k = 1; k < m_n; ++k) {
329 aout[k] = (a[k - 1] - a[k + 1]) / double(2 * k);
330 }
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));
333 double d = (m_end - m_start) / 2;
334 std::transform(aout.begin(), aout.end(), aout.begin(), std::bind(std::multiplies<double>(), _1, d));
335 return ChebfunBase_sptr(new ChebfunBase(m_n + 1, m_start, m_end));
336}
337
357template <class FunctionType>
358ChebfunBase_sptr ChebfunBase::bestFitTempl(double start, double end, FunctionType f, std::vector<double> &p,
359 std::vector<double> &a, double maxA, double tolerance, size_t maxSize) {
360
361 std::vector<double> p1, p2;
362 const size_t n0 = 8;
363 bool calcMaxA = maxA == 0.0;
364 if (tolerance == 0.0)
366 // number of non-zero a-coefficients for checking if the function is a
367 // polynomial
368 size_t countNonZero = n0 / 2;
369 if (maxSize == 0)
370 maxSize = g_maxNumberPoints;
371 for (size_t n = n0; n < maxSize; n *= 2) {
372 // value of n must be even! or everything breaks!
373 ChebfunBase base(n, start, end, tolerance);
374 if (p2.empty()) {
375 p2 = base.fit(f);
376 } else {
377 p2 = base.fitOdd(f, p1);
378 }
379 a = base.calcA(p2);
380 if (calcMaxA) {
381 maxA = 0.0;
382 for (double coeff : a) {
383 double tmp = fabs(coeff);
384 if (tmp > maxA) {
385 maxA = tmp;
386 }
387 }
388 }
389 if (ChebfunBase::hasConverged(a, maxA, tolerance)) {
390 // cut off the trailing a-values that are below the tolerance
391
392 size_t m = n + 1;
393 size_t dm = 0;
394 while (dm < m - 2 && ChebfunBase::hasConverged(a, maxA, tolerance, dm)) {
395 ++dm;
396 }
397 // restore a to the converged state
398 if (dm > 0)
399 --dm;
400 m -= dm;
401
402 // remove possible negligible trailing coefficients left over
403 while (m > 2 && fabs(a[m - 1]) / maxA < tolerance) {
404 --m;
405 }
406
407 if (m != n + 1) {
408 auto newBase = ChebfunBase_sptr(new ChebfunBase(m - 1, start, end, tolerance));
409 a.resize(m);
410 p = newBase->calcP(a);
411 return newBase;
412 } else {
413 p.assign(p2.begin(), p2.end());
414 return ChebfunBase_sptr(new ChebfunBase(base));
415 }
416 }
417 size_t nNonZero = a.size();
418 for (auto i = a.rbegin(); i != a.rend(); ++i) {
419 if (*i == 0.0) {
420 nNonZero -= 1;
421 } else {
422 break;
423 }
424 }
425 if (nNonZero == countNonZero) {
426 // it is a polynomial
427 if (countNonZero < 2)
428 countNonZero = 2;
429 auto newBase = ChebfunBase_sptr(new ChebfunBase(countNonZero - 1, start, end));
430 a.resize(countNonZero);
431 p = newBase->calcP(a);
432 return newBase;
433 } else {
434 countNonZero = nNonZero;
435 }
436 std::swap(p1, p2);
437 }
438 p.clear();
439 a.clear();
440 a.emplace_back(maxA);
441 return ChebfunBase_sptr();
442}
443
445ChebfunBase_sptr ChebfunBase::bestFit(double start, double end, ChebfunFunctionType f, std::vector<double> &p,
446 std::vector<double> &a, double maxA, double tolerance, size_t maxSize) {
447 return bestFitTempl(start, end, std::move(f), p, a, maxA, tolerance, maxSize);
448}
449
451ChebfunBase_sptr ChebfunBase::bestFit(double start, double end, const API::IFunction &f, std::vector<double> &p,
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);
454}
455
461std::vector<double> ChebfunBase::linspace(size_t n) const {
462 std::vector<double> space(n);
463 double x = m_start;
464 const double dx = width() / double(n - 1);
465 for (double &s : space) {
466 s = x;
467 x += dx;
468 }
469 space.back() = m_end;
470 return space;
471}
472
478std::vector<double> ChebfunBase::calcA(const std::vector<double> &p) const {
479 const size_t nn = m_n + 1;
480
481 if (p.size() != nn) {
482 throw std::invalid_argument("ChebfunBase: function vector must have same size as the base.");
483 }
484
485 std::vector<double> a(nn);
486
489 // for(int i = 0; i < nn; ++i)
490 // {
491 // double t = 0.;
492 // for(int j = 0; j <= m_n; j++)
493 // {
494 // double p1 = p[m_n - j];
495 // if (j== 0 || j == m_n) p1 /= 2;
496 // t += cos(M_PI*i*(double(j))/m_n)*p1;
497 // }
498 // a[i] = 2*t/m_n;
499 // //if (i == 0) m_a[0] /= 2;
500 // }
501 // a[0] /= 2;
502 // a[m_n] /= 2;
503 // return a;
505
506 if (m_n > 0) {
507 // This is a magic trick which uses real fft to do the above cosine
508 // transform
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);
512
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);
515 gsl_fft_real_transform(&tmp[0], 1, 2 * m_n, wavetable, workspace);
516 gsl_fft_real_wavetable_free(wavetable);
517 gsl_fft_real_workspace_free(workspace);
518
519 HalfComplex fc(&tmp[0], tmp.size());
520 for (size_t i = 0; i < nn; ++i) {
521 a[i] = fc.real(i) / double(m_n);
522 }
523 a[0] /= 2;
524 a[m_n] /= 2;
525 // End of the magic trick
526 } else {
527 a[0] = p[0];
528 }
529 return a;
530}
531
538std::vector<double> ChebfunBase::calcP(const std::vector<double> &a) const {
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());
543 }
544 std::vector<double> p(m_n + 1);
545
546 if (m_n > 0) {
547 size_t nn = m_n + 1;
548 std::vector<double> tmp(m_n * 2);
549 HalfComplex fc(&tmp[0], tmp.size());
550 for (size_t i = 0; i < nn; ++i) {
551 double d = a[i] / 2;
552 if (i == 0 || i == nn - 1)
553 d *= 2;
554 fc.set(i, d, 0.0);
555 }
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);
558
559 gsl_fft_halfcomplex_transform(tmp.data(), 1, 2 * m_n, wavetable, workspace);
560
561 gsl_fft_halfcomplex_wavetable_free(wavetable);
562 gsl_fft_real_workspace_free(workspace);
563
564 std::reverse_copy(tmp.begin(), tmp.begin() + nn, p.begin());
565 } else {
566 p[0] = a[0];
567 }
568 return p;
569}
570
576std::vector<double> ChebfunBase::fit(ChebfunFunctionType f) const {
577 std::vector<double> res(size());
578 std::transform(m_x.begin(), m_x.end(), res.begin(), std::move(f));
579 return res;
580}
581
587std::vector<double> ChebfunBase::fit(const API::IFunction &f) const {
588 API::FunctionDomain1DView x(m_x.data(), m_x.size());
590 f.function(x, y);
591 return y.toVector();
592}
593
601std::vector<double> ChebfunBase::fitOdd(const ChebfunFunctionType &f, std::vector<double> &p) const {
602 assert(size() == p.size() * 2 - 1);
603 assert(size() % 2 == 1);
604 auto &xp = xPoints();
605 std::vector<double> res(xp.size());
606 auto it2 = res.begin();
607 auto it1 = p.begin();
608 // xp is odd-sized so the loop is ok
609 for (auto x = xp.begin() + 1; x != xp.end(); x += 2, ++it1, ++it2) {
610 *it2 = *it1; // one value from the previous iteration
611 ++it2;
612 *it2 = f(*x); // one new value
613 }
614 *(res.end() - 1) = p.back();
615 return res;
616}
617
625std::vector<double> ChebfunBase::fitOdd(const API::IFunction &f, std::vector<double> &pEven) const {
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());
631 // m_x is odd-sized so the loop is ok
632 for (auto x = m_x.begin() + 1; x != m_x.end(); x += 2) {
633 xOdd.emplace_back(*x);
634 }
635
636 API::FunctionDomain1DView x(xOdd.data(), xOdd.size());
638 f.function(x, y);
639 pOdd = y.toVector();
640
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];
645 }
646 res.back() = pEven.back();
647 return res;
648}
649
656std::vector<double> ChebfunBase::roots(const std::vector<double> &a) const {
657 std::vector<double> r;
658 // build the companion matrix
659 size_t N = order();
660 // ensure that the highest order coeff is > epsilon
661 const double epsilon = std::numeric_limits<double>::epsilon() * 100;
662 while (N > 0 && fabs(a[N]) < epsilon) {
663 --N;
664 }
665
666 if (N == 0)
667 return r; // function is a constant
668
669 const size_t N2 = 2 * N;
670 EigenMatrix C(N2, N2);
671 C.zero();
672 const double an = a[N];
673
674 const size_t lasti = N2 - 1;
675 for (size_t i = 0; i < N; ++i) {
676 if (i > 0) {
677 C.set(i, i - 1, 1.0);
678 }
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;
682 if (i == 0)
683 tmp *= 2;
684 C.set(N + i, lasti, tmp);
685 }
686
687 gsl_vector_complex *eval = gsl_vector_complex_alloc(N2);
688 auto workspace = gsl_eigen_nonsymm_alloc(N2);
689
690 EigenMatrix C_tr(C.tr());
691 gsl_matrix_view C_tr_gsl = getGSLMatrixView(C_tr.mutator());
692 gsl_eigen_nonsymm(&C_tr_gsl.matrix, eval, workspace);
693 gsl_eigen_nonsymm_free(workspace);
694
695 const double Dx = endX() - startX();
696 bool isFirst = true;
697 double firstIm = 0;
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) {
704 isFirst = true;
705 continue;
706 }
707 if (isFirst) {
708 isFirst = false;
709 firstIm = im;
710 } else {
711 if (im + firstIm < 1e-10) {
712 double x = startX() + (re + 1.0) / 2.0 * Dx;
713 r.emplace_back(x);
714 }
715 isFirst = true;
716 }
717 }
718 gsl_vector_complex_free(eval);
719
720 return r;
721}
722
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);
735
736 // interpolate yvalues at the x-points of this base
737 auto ix = xvalues.begin();
738 auto xbegin = ix;
739 auto xend = xvalues.end();
740 for (size_t i = 0; i < n; ++i) {
741 if (ix == xvalues.end()) {
742 break;
743 }
744 double x = m_x[i];
745 auto ix0 = std::lower_bound(ix, xend, x);
746 if (ix0 == xend)
747 continue;
748 auto j = std::distance(xbegin, ix0);
749 if (j > 0) {
750 y[i] = yvalues[j - 1] + (x - xvalues[j - 1]) / (xvalues[j] - xvalues[j - 1]) * (yvalues[j] - yvalues[j - 1]);
751 ix = ix0;
752 } else {
753 y[i] = yvalues[0];
754 }
755 }
756
757 const double guessSignalToNoiseRatio = 1e15;
758 auto a = calcA(y);
759
760 std::vector<double> powerSpec(n);
761 assert(powerSpec.size() == n);
762 // convert the a-coeffs to power spectrum wich is the base of the Wiener
763 // filter
764 std::transform(a.begin(), a.end(), powerSpec.begin(), AbsValue);
765
766 // estimate power spectrum's noise as the average of its high frequency half
767 double noise = std::accumulate(powerSpec.begin() + n / 2, powerSpec.end(), 0.0);
768 noise /= static_cast<double>(n / 2);
769
770 // index of the maximum element in powerSpec
771 const size_t imax =
772 static_cast<size_t>(std::distance(powerSpec.begin(), std::max_element(powerSpec.begin(), powerSpec.end())));
773
774 if (noise == 0.0) {
775 noise = powerSpec[imax] / guessSignalToNoiseRatio;
776 }
777
778 // std::cerr << "Maximum signal " << powerSpec[imax] << '\n';
779 // std::cerr << "Noise " << noise << '\n';
780 // std::cerr << noise / powerSpec[imax] << '\n';
781
782 // storage for the Wiener filter, initialized with 0.0's
783 std::vector<double> wf(n);
784
785 // The filter consists of two parts:
786 // 1) low frequency region, from 0 until the power spectrum falls to the
787 // noise level, filter is calculated
788 // from the power spectrum
789 // 2) high frequency noisy region, filter is a smooth function of frequency
790 // decreasing to 0
791
792 // the following code is an adaptation of a fortran routine with modifications
793 // noise starting index
794 size_t i0 = 0;
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;
797 if (av < noise) {
798 i0 = 3 * i;
799 break;
800 }
801 }
802 // intermediate variables
803 double xx = 0.0;
804 double xy = 0.0;
805 double ym = 0.0;
806 // low frequency filter values: the higher the power spectrum the closer the
807 // filter to 1.0
808 // std::cerr << "i0=" << i0 << '\n';
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);
814 xx += j * j;
815 xy += j * cd2;
816 ym += cd2;
817 }
818
819 // i0 should always be > 0 but in case something goes wrong make a check
820 if (i0 > 0) {
821 // std::cerr << "Noise start index " << i0 << ' ' << n << '\n';
822 if (noise / powerSpec[imax] > 0.01 && i0 > n / 2) {
823 // std::cerr << "There is too much noise: no smoothing.\n";
824 return y;
825 }
826
827 // high frequency filter values: smooth decreasing function
828 auto ri0f = static_cast<double>(i0 + 1);
829 double xm = (1.0 + ri0f) / 2;
830 ym /= ri0f;
831 double a1 = (xy - ri0f * xm * ym) / (xx - ri0f * xm * xm);
832 double b1 = ym - a1 * xm;
833
834 // std::cerr << "(a1,b1) = (" << a1 << ',' << b1 << ')' << '\n';
835
836 // calculate coeffs of a cubic c3*i^3 + c2*i^2 + c1*i + c0
837 // which will replace the linear a1*i + b1 in building the
838 // second part of the filter
839 double c0, c1, c2, c3;
840 {
841 auto x0 = double(i0 + 1);
842 auto x1 = double(n + 1);
843 double sigma = g_tolerance / noise / 10;
844 double s = sigma / (1.0 - sigma);
845 double m1 = log(s);
846 double m0 = a1 * x0 + b1;
847 if (a1 < 0.0) {
848 c3 = 0.0;
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;
852 } else {
853 c3 = 0.0;
854 c2 = 0.0;
855 c1 = (m1 - m0) / (x1 - x0);
856 c0 = m0 - c1 * x0;
857 double tmp = exp(c1 * double(i0 + 2) + c0);
858 tmp = tmp / (1.0 + tmp);
859 if (tmp > 0.1) {
860 m0 = 0.0;
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;
866 }
867 }
868 }
869
870 // std::cerr << "(c2,c1,c0) = (" << c2 << ',' << c1 << ',' << c0 << ')' <<
871 // '\n';
872
873 for (size_t i = i0; i < n; ++i) {
874 // double s = exp(a1*static_cast<double>(i+1)+b1);
875 auto s = double(i + 1);
876 s = c0 + s * (c1 + s * (c2 + s * c3));
877 if (s > 100.0) {
878 wf[i] = 1.0;
879 } else {
880 s = exp(s);
881 wf[i] = s / (1.0 + s);
882 }
883 }
884 }
885
886 std::transform(a.begin(), a.end(), wf.begin(), a.begin(), std::multiplies<double>());
887 y = calcP(a);
888
889 return y;
890}
891
892} // namespace Mantid::CurveFitting::Functions
gsl_vector * tmp
gsl_fft_real_wavetable * wavetable
double value
The value of the point.
Definition: FitMW.cpp:51
double sigma
Definition: GetAllEi.cpp:156
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
#define fabs(x)
Definition: Matrix.cpp:22
double tolerance
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.
Definition: IFunction.h:163
virtual void function(const FunctionDomain &domain, FunctionValues &values) const =0
Evaluates the function for all arguments in the domain.
A wrapper around Eigen::Matrix.
Definition: EigenMatrix.h:33
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.
Definition: EigenMatrix.h:56
void zero()
Set all elements to zero.
The ChebfunBase class provides a base for function approximation with Chebyshev polynomials.
Definition: ChebfunBase.h:66
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 size() const
Get the size of the base which is the number of x-points.
Definition: ChebfunBase.h:76
double m_start
Start of the interval.
Definition: ChebfunBase.h:159
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.
Definition: ChebfunBase.cpp:84
std::vector< double > m_x
The x-points.
Definition: ChebfunBase.h:163
std::vector< double > m_bw
The barycentric weights.
Definition: ChebfunBase.h:165
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.
Definition: ChebfunBase.h:171
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.
Definition: ChebfunBase.h:169
double width() const
Get the width of the interval.
Definition: ChebfunBase.h:82
const std::vector< double > & integrationWeights() const
Get a reference to the integration weights.
Definition: ChebfunBase.cpp:72
ChebfunBase(size_t n, double start, double end, double tolerance=0.0)
Constructor.
Definition: ChebfunBase.cpp:51
double endX() const
End of the interval.
Definition: ChebfunBase.h:80
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.
Definition: ChebfunBase.h:167
double startX() const
Start of the interval.
Definition: ChebfunBase.h:78
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.
Definition: ChebfunBase.h:84
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.
Definition: ChebfunBase.h:120
size_t order() const
Get the polynomial order of this base.
Definition: ChebfunBase.h:74
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.
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.
Definition: HalfComplex.h:19
double real(size_t i) const
The real part of i-th transform coefficient.
Definition: HalfComplex.h:34
void set(size_t i, const double &re, const double &im)
Set a new value for i-th complex coefficient.
Definition: HalfComplex.h:58
std::shared_ptr< ChebfunBase > ChebfunBase_sptr
Definition: ChebfunBase.h:174
std::function< double(double)> ChebfunFunctionType
Type of the approximated function.
Definition: ChebfunBase.h:26
gsl_matrix_view getGSLMatrixView(map_type &tr)
take data from an Eigen Matrix and return a transposed a gsl view.
Definition: GSLFunctions.h:56
STL namespace.