Mantid
Loading...
Searching...
No Matches
VectorHelper.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 +
7#include <cmath>
8#include <stdexcept>
9
10#include "MantidKernel/DllConfig.h"
11#include "MantidKernel/Memory.h"
13#include <algorithm>
14#include <boost/algorithm/string.hpp>
15#include <numeric>
16#include <sstream>
17
18using std::size_t;
19
20namespace {
21// edge checks
23struct EdgeCheck { // for polymorphic access to functors
25 virtual bool operator()(double, double, double) const = 0;
26 virtual ~EdgeCheck() = default;
27};
28
29// Standard check, for linear, log, and power
30struct StandardEdgeCheck : EdgeCheck {
31 StandardEdgeCheck(double lastBinCoef) : m_lastBinCoef(lastBinCoef) {}
32 bool operator()(double xcurr, double xs, double rightEdge) const override {
33 return xcurr + (1. + m_lastBinCoef) * xs <= rightEdge;
34 }
35
36private:
37 double m_lastBinCoef;
38};
39// Reverse log check, for reverse log only
40struct ReverseLogEdgeCheck : EdgeCheck {
41 bool operator()(double xcurr, double xs, double leftEdge) const override { return xcurr + 2. * xs >= leftEdge; }
42};
43
44// bin width functions
46struct BinWidth { // for polymorphic access to functors
48 virtual double operator()(double, double) const = 0;
49 virtual ~BinWidth() = default;
50};
51
52// Linear bin width is just the alpha parameter
53struct LinearBinWidth : BinWidth {
54 double operator()([[maybe_unused]] double xcurr, double alpha) const override { return alpha; }
55};
56// Log bin width is current position times the alpha parameter
57struct LogBinWidth : BinWidth {
58 double operator()(double xcurr, double alpha) const override { return xcurr * alpha; }
59};
60
61// REVERSE LOGARITHMIC
62// we go through a reverse log bin by starting from its end, and working our way back to the beginning
63// this way we can define the bins in a recurring way, and with a more obvious closeness with the usual log.
64struct ReverseLogBinWidth : BinWidth {
65 ReverseLogBinWidth(double leftEdge, double rightEdge) : m_leftEdge(leftEdge), m_rightEdge(rightEdge) {}
66 double operator()(double xcurr, double alpha) const override { return (xcurr - m_rightEdge - m_leftEdge) * alpha; }
67
68private:
69 double m_leftEdge;
70 double m_rightEdge;
71};
72// Power bin width is the alpha parameter divided by the current index to the power
73struct PowerBinWidth : BinWidth {
74 PowerBinWidth(std::size_t &currDiv, double power) : m_currDiv(&currDiv), m_power(power) {}
75 double operator()([[maybe_unused]] double xcurr, double alpha) const override {
76 return alpha * std::pow((*m_currDiv)++, -m_power);
77 }
78
79private:
80 std::size_t *const m_currDiv; // the pointed-to changes, but not the pointer
81 double m_power;
82};
83} // namespace
84
86
88void validateRebinParameters(std::vector<double> const &params, bool const isPower) {
89 size_t numParams = params.size();
90 if (numParams < 3 || numParams % 2 != 1) {
91 throw std::runtime_error("validateRebinParameters: Invalid number of rebin parameters (" +
92 std::to_string(numParams) + ")! Must be odd number of parameters, 3 or more.");
93 }
94 double previousEdge = params[0];
95 for (size_t i = 1; i < numParams - 1; i += 2) {
96 double const binWidth = params[i];
97 double const nextEdge = params[i + 1];
98 if (previousEdge >= nextEdge) {
99 throw std::runtime_error(
100 "validateRebinParameters: Bin boundary values must be given in order of increasing value! " +
101 std::to_string(previousEdge) + " >= " + std::to_string(nextEdge));
102 } else if (binWidth == 0) {
103 throw std::runtime_error("validateRebinParameters: Bin width cannot be zero! Found in range " +
104 std::to_string(previousEdge) + " to " + std::to_string(nextEdge));
105 } else if (binWidth < 0 && previousEdge <= 0) {
106 throw std::runtime_error("Bin boundaries must be positive for logarithmic binning. Got " +
107 std::to_string(previousEdge) + " to " + std::to_string(nextEdge));
108 } else if (binWidth < 0 && isPower) {
109 throw std::runtime_error(
110 "validateRebinParameters: Cannot use power binning with negative bin width! Bin width was " +
111 std::to_string(binWidth));
112 }
113 previousEdge = nextEdge;
114 }
115}
116
121std::size_t estimateNumberOfBins(std::vector<double> const &params, double const power) {
122 bool const isPower = power > 0 && power <= 1;
123 validateRebinParameters(params, isPower);
124 double numBins = 1.; // always start with 1 left edge / fencepost
125 double previousEdge = params[0];
126 for (size_t i = 1; i < params.size() - 1; i += 2) {
127 double const binWidth = params[i];
128 double const nextEdge = params[i + 1];
129 if (binWidth < 0) {
130 // LOGARITHMIC BINNING
131 // NOTE log or reverse log should have approximately the same number of bins here
132 numBins += std::ceil(std::log(nextEdge / previousEdge) / std::log(1. - binWidth));
133 } else if (isPower) {
134 // POWER BINNING
135 // formulas taken from Rebin::validateInputs
136 if (power == 1) {
137 double constexpr eulerMascheroni = 0.5772156649; // Euler-Mascheroni constant
138 numBins += std::exp((nextEdge - previousEdge) / binWidth - eulerMascheroni);
139 } else {
140 numBins += std::pow(((nextEdge - previousEdge) / binWidth) * (1 - power) + 1, 1 / (1 - power));
141 }
142 } else if (binWidth > 0) {
143 // LINEAR BINNING
144 numBins += std::ceil((nextEdge - previousEdge) / binWidth);
145 }
146 previousEdge = nextEdge;
147 }
148 if (numBins >= static_cast<double>(std::numeric_limits<std::size_t>::max())) {
149 throw std::runtime_error(
150 "estimateNumberOfBins: The number of bins requested exceeds the maximum storable by size_t.");
151 }
152 return static_cast<size_t>(numBins);
153}
154
169std::size_t createAxisFromRebinParams(const std::vector<double> &params, std::vector<double> &xnew,
170 const bool resize_xnew, const bool full_bins_only, const double xMinHint,
171 const double xMaxHint, const bool useReverseLogarithmic, const double power) {
172
173 // correctly expand the parameters
174 std::vector<double> tmp;
175 if (params.size() == 1) {
176 if (std::isnan(xMinHint) || std::isnan(xMaxHint)) {
177 throw std::runtime_error(
178 "createAxisFromRebinParams: xMinHint and xMaxHint must be supplied if params contains only the bin width.");
179 }
180 tmp = {xMinHint, params.front(), xMaxHint};
181 }
182 std::vector<double> const &fullParams = params.size() == 1 ? tmp : params;
183
184 // This coefficient represents the maximum difference between the size of the last bin and all
185 // the other bins.
186 // For full_bin_only, we want it so that last bin couldn't be smaller than the previous bin
187 double const lastBinCoef = full_bins_only ? 1.0 : 0.25;
188 // whether to use power binning
189 bool isPower = power > 0 && power <= 1;
190
191 // if resize_xnew is true, prepare the vector
192 double xcurr = fullParams[0];
193 xnew.clear();
194 if (resize_xnew) {
195 // call to estimateNumberOfBins will also validate the parameters
196 size_t neededSize = estimateNumberOfBins(fullParams, power);
197 /* detect potential memory overflows
198 * TODO find a way to check against actual available system memory
199 * NOTE use of MemoryStats.availMem() was considered, but introduces large overhead into this hot path
200 */
201 if (neededSize >= xnew.max_size()) {
202 throw std::runtime_error(
203 "createAxisFromRebinParams: The number of bins requested exceeds the maximum storable by the output vector.");
204 }
205 xnew.reserve(neededSize);
206 xnew.push_back(xcurr);
207 } else {
208 // validate the parameters, but don't calculate needed size
209 validateRebinParameters(fullParams, isPower);
210 }
211
212 std::size_t currDiv = 1;
213
214 // for each range specified, fill in the axis with correct bin edges
215 std::size_t inew = 1; // we start with 1, the leftmost edge
216 double leftEdge = fullParams[0];
217 for (std::size_t i = 1; i <= fullParams.size() - 1; i += 2) {
218 double binParam = fullParams[i];
219 double rightEdge = fullParams[i + 1];
220 double alpha = std::fabs(binParam);
221
222 // functions for calculating bin width and checking if within range
223 std::unique_ptr<BinWidth> binWidth(nullptr);
224 std::unique_ptr<EdgeCheck> edgeCheck(nullptr);
225 if (binParam < 0.0 && useReverseLogarithmic) {
226 // REVERSE LOGARITHMIC
227 binWidth = std::make_unique<ReverseLogBinWidth>(leftEdge, rightEdge);
228 edgeCheck = std::make_unique<ReverseLogEdgeCheck>();
229 // for reverse log, we start at the right edge and work back to left; to use the same loop, swap the edges
230 // the final edge will be added after the loop, fullParams[i+1]
231 xcurr = rightEdge;
232 std::swap(leftEdge, rightEdge);
233 } else if (binParam < 0.0) {
234 // LOGARITHMIC
235 binWidth = std::make_unique<LogBinWidth>();
236 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
237 } else if (isPower) {
238 // POWER
239 binWidth = std::make_unique<PowerBinWidth>(currDiv, power);
240 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
241 } else {
242 // LINEAR
243 binWidth = std::make_unique<LinearBinWidth>();
244 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
245 }
246
247 double xs = (*binWidth)(xcurr, alpha);
248 while ((*edgeCheck)(xcurr, xs, rightEdge) || !std::isfinite(xs)) { // let errors fall through
249 if (xs == 0.0) {
250 // Someone gave a 0-sized step! What a dope.
251 throw std::runtime_error("Invalid binning step provided! Can't create binning axis.");
252 } else if (!std::isfinite(xs)) {
253 throw std::runtime_error("An infinite or NaN value was found in the binning parameters.");
254 }
255 xcurr = xcurr + xs;
256 if (resize_xnew) {
257 xnew.push_back(xcurr);
258 }
259 inew++;
260 // prepare for next step
261 xs = (*binWidth)(xcurr, alpha);
262 }
263
264 // handle last edge specially, depending on if full_bins_only is set
265 if (!useReverseLogarithmic && full_bins_only) {
266 // For full_bins_only, we want to add one more full bin, so that last bin is not bigger than the previous one
267 xcurr = xcurr + xs;
268 } else {
269 // For non full_bins_only, finish by adding as much as is left from the range
270 // NOTE for reverse log, the left edge was already added, so we can instead jump to the right edge of this range
271 xcurr = fullParams[i + 1];
272 }
273 if (resize_xnew) {
274 xnew.push_back(xcurr);
275 }
276 inew++;
277 // prepare for next range
278 leftEdge = xcurr;
279 }
280 // if using reverse log, some bins were added in wrong order; so sort them
281 if (useReverseLogarithmic) {
282 std::sort(xnew.begin(), xnew.end());
283 }
284 return inew;
285}
286
306void rebin(std::span<double const> xold, std::span<double const> yold, std::span<double const> eold,
307 std::span<double const> xnew, std::span<double> ynew, std::span<double> enew, bool distribution,
308 bool addition) {
309 // Make sure y and e vectors are of correct sizes
310 const size_t size_xold = xold.size();
311 if (size_xold != (yold.size() + 1) || size_xold != (eold.size() + 1))
312 throw std::runtime_error("rebin: old y and error vectors should be of same "
313 "size & 1 shorter than x");
314 const size_t size_xnew = xnew.size();
315 if (size_xnew != (ynew.size() + 1) || size_xnew != (enew.size() + 1))
316 throw std::runtime_error("rebin: new y and error vectors should be of same "
317 "size & 1 shorter than x");
318
319 size_t size_yold = yold.size();
320 size_t size_ynew = ynew.size();
321
322 if (!addition) {
323 // Make sure ynew & enew contain zeroes
324 std::fill(ynew.begin(), ynew.end(), 0.0);
325 std::fill(enew.begin(), enew.end(), 0.0);
326 }
327
328 size_t iold = 0, inew = 0;
329 double width;
330
331 while ((inew < size_ynew) && (iold < size_yold)) {
332 double xo_low = xold[iold];
333 double xo_high = xold[iold + 1];
334 double xn_low = xnew[inew];
335 double xn_high = xnew[inew + 1];
336 if (xn_high <= xo_low)
337 inew++; /* old and new bins do not overlap */
338 else if (xo_high <= xn_low)
339 iold++; /* old and new bins do not overlap */
340 else {
341 // delta is the overlap of the bins on the x axis
342 // delta = std::min(xo_high, xn_high) - std::max(xo_low, xn_low);
343 double delta = xo_high < xn_high ? xo_high : xn_high;
344 delta -= xo_low > xn_low ? xo_low : xn_low;
345 width = xo_high - xo_low;
346 if ((delta <= 0.0) || (width <= 0.0)) {
347 // No need to throw here, just return (ynew & enew will be empty)
348 // throw std::runtime_error("rebin: no bin overlap detected");
349 return;
350 }
351 /*
352 * yoldp contains counts/unit time, ynew contains counts
353 * enew contains counts**2
354 * ynew has been filled with zeros on creation
355 */
356 if (distribution) {
357 // yold/eold data is distribution
358 ynew[inew] += yold[iold] * delta;
359 // this error is calculated in the same way as opengenie
360 enew[inew] += eold[iold] * eold[iold] * delta * width;
361 } else {
362 // yold/eold data is not distribution
363 // do implicit division of yold by width in summing.... avoiding the
364 // need for temporary yold array
365 // this method is ~7% faster and uses less memory
366 ynew[inew] += yold[iold] * delta / width; // yold=yold/width
367 // eold=eold/width, so divide by width**2 compared with distribution
368 // calculation
369 enew[inew] += eold[iold] * eold[iold] * delta / width;
370 }
371 if (xn_high > xo_high) {
372 iold++;
373 } else {
374 inew++;
375 }
376 }
377 }
378
379 if (!addition) // If using the addition facility, have to do bin width and
380 // sqrt errors externally
381 {
382 if (distribution) {
383 /*
384 * convert back to counts/unit time
385 */
386 for (size_t i = 0; i < size_ynew; ++i) {
387 {
388 width = xnew[i + 1] - xnew[i];
389 if (width != 0.0) {
390 ynew[i] /= width;
391 enew[i] = sqrt(enew[i]) / width;
392 } else {
393 throw std::invalid_argument("rebin: Invalid output X array, contains consecutive X values");
394 }
395 }
396 }
397 } else {
398 // non-distribution, just square root final error value
399 using pf = double (*)(double);
400 pf uf = std::sqrt;
401 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
402 }
403 }
404}
405
406//-------------------------------------------------------------------------------------------------
423void rebinHistogram(std::span<double const> xold, std::span<double const> yold, std::span<double const> eold,
424 std::span<double const> xnew, std::span<double> ynew, std::span<double> enew, bool addition) {
425 // Make sure y and e vectors are of correct sizes
426 const size_t size_yold = yold.size();
427 if (xold.size() != (size_yold + 1) || size_yold != eold.size())
428 throw std::runtime_error("rebin: y and error vectors should be of same size & 1 shorter than x");
429 const size_t size_ynew = ynew.size();
430 if (xnew.size() != (size_ynew + 1) || size_ynew != enew.size())
431 throw std::runtime_error("rebin: y and error vectors should be of same size & 1 shorter than x");
432
433 // If not adding to existing vectors, make sure ynew & enew contain zeroes
434 if (!addition) {
435 std::fill(ynew.begin(), ynew.end(), 0.0);
436 std::fill(enew.begin(), enew.end(), 0.0);
437 }
438
439 // Find the starting points to avoid wasting time processing irrelevant bins
440 size_t iold = 0, inew = 0; // iold/inew is the bin number under consideration
441 // (counting from 1, so index+1)
442 if (xnew.front() > xold.front()) {
443 auto it = std::upper_bound(xold.begin(), xold.end(), xnew.front());
444 if (it == xold.end())
445 return;
446 // throw std::runtime_error("No overlap: max of X-old < min of X-new");
447 iold = std::distance(xold.begin(), it) - 1; // Old bin to start at (counting from 0)
448 } else {
449 auto it = std::upper_bound(xnew.begin(), xnew.end(), xold.front());
450 if (it == xnew.end())
451 return;
452 // throw std::runtime_error("No overlap: max of X-new < min of X-old");
453 inew = std::distance(xnew.begin(), it) - 1; // New bin to start at (counting from 0)
454 }
455
456 double frac, fracE;
457 double oneOverWidth, overlap;
458 double temp;
459
460 // loop over old vector from starting point calculated above
461 for (; iold < size_yold; ++iold) {
462 double xold_of_iold_p_1 = xold[iold + 1]; // cache for speed
463 // If current old bin is fully enclosed by new bin, just unload the counts
464 if (xold_of_iold_p_1 <= xnew[inew + 1]) {
465 ynew[inew] += yold[iold];
466 temp = eold[iold];
467 enew[inew] += temp * temp;
468 // If the upper bin boundaries were equal, then increment inew
469 if (xold_of_iold_p_1 == xnew[inew + 1])
470 inew++;
471 } else {
472 double xold_of_iold = xold[iold]; // cache for speed
473 // This is the counts per unit X in current old bin
474 oneOverWidth = 1. / (xold_of_iold_p_1 - xold_of_iold); // cache 1/width to speed things up
475 frac = yold[iold] * oneOverWidth;
476 temp = eold[iold];
477 fracE = temp * temp * oneOverWidth;
478
479 // Now loop over bins in new vector overlapping with current 'old' bin
480 while (inew < size_ynew && xnew[inew + 1] <= xold_of_iold_p_1) {
481 overlap = xnew[inew + 1] - std::max(xnew[inew], xold_of_iold);
482 ynew[inew] += frac * overlap;
483 enew[inew] += fracE * overlap;
484 ++inew;
485 }
486
487 // Stop if at end of new X range
488 if (inew == size_ynew)
489 break;
490
491 // Unload the rest of the current old bin into the current new bin
492 overlap = xold_of_iold_p_1 - xnew[inew];
493 ynew[inew] += frac * overlap;
494 enew[inew] += fracE * overlap;
495 }
496 } // loop over old bins
497
498 if (!addition) // If this used to add at the same time then not necessary
499 // (should be done externally)
500 {
501 // Now take the root-square of the errors
502 using pf = double (*)(double);
503 pf uf = std::sqrt;
504 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
505 }
506}
507
508//-------------------------------------------------------------------------------------------------
514void convertToBinCentre(std::span<double const> bin_edges, std::vector<double> &bin_centres) {
515 const std::vector<double>::size_type npoints = bin_edges.size();
516 if (bin_centres.size() != npoints) {
517 bin_centres.resize(npoints);
518 }
519
520 // The custom binary function modifies the behaviour of the algorithm to
521 // compute the average of
522 // two adjacent bin boundaries
523 std::adjacent_difference(bin_edges.begin(), bin_edges.end(), bin_centres.begin(), SimpleAverage<double>());
524 // The algorithm copies the first element of the input to the first element of
525 // the output so we need to
526 // remove the first element of the output
527 bin_centres.erase(bin_centres.begin());
528}
529
530//-------------------------------------------------------------------------------------------------
546void convertToBinBoundary(std::span<double const> bin_centers, std::vector<double> &bin_edges) {
547 const auto n = bin_centers.size();
548
549 // Special case empty input: output is also empty
550 if (n == 0) {
551 bin_edges.resize(0);
552 return;
553 }
554
555 bin_edges.resize(n + 1);
556
557 // Special case input of size one: we have no means of guessing the bin size,
558 // set it to 1.
559 if (n == 1) {
560 bin_edges[0] = bin_centers[0] - 0.5;
561 bin_edges[1] = bin_centers[0] + 0.5;
562 return;
563 }
564
565 for (size_t i = 0; i < n - 1; ++i) {
566 bin_edges[i + 1] = 0.5 * (bin_centers[i] + bin_centers[i + 1]);
567 }
568
569 bin_edges[0] = bin_centers[0] - (bin_edges[1] - bin_centers[0]);
570
571 bin_edges[n] = bin_centers[n - 1] + (bin_centers[n - 1] - bin_edges[n - 1]);
572}
573
584size_t indexOfValueFromCenters(std::span<double const> bin_centers, const double value) {
585 int index = indexOfValueFromCentersNoThrow(bin_centers, value);
586 if (index >= 0)
587 return static_cast<size_t>(index);
588 else
589 throw std::out_of_range("indexOfValue - value out of range");
590}
591
592int indexOfValueFromCentersNoThrow(std::span<double const> bin_centers, const double value) {
593 if (bin_centers.empty()) {
594 throw std::out_of_range("indexOfValue - vector is empty");
595 }
596 if (bin_centers.size() == 1) {
597 // no mean to guess bin size, assuming 1
598 if (value < bin_centers[0] - 0.5 || value > bin_centers[0] + 0.5) {
599 return -1;
600 } else {
601 return 0;
602 }
603 } else {
604 const size_t n = bin_centers.size();
605 const double firstBinLowEdge = bin_centers[0] - 0.5 * (bin_centers[1] - bin_centers[0]);
606 const double lastBinHighEdge = bin_centers[n - 1] + 0.5 * (bin_centers[n - 1] - bin_centers[n - 2]);
607 if (value < firstBinLowEdge || value > lastBinHighEdge) {
608 return -1;
609 } else {
610 const auto it = std::lower_bound(bin_centers.begin(), bin_centers.end(), value);
611 if (it == bin_centers.end()) {
612 return static_cast<int>(n - 1);
613 }
614 size_t binIndex = std::distance(bin_centers.begin(), it);
615 if (binIndex > 0 &&
616 value < bin_centers[binIndex - 1] + 0.5 * (bin_centers[binIndex] - bin_centers[binIndex - 1])) {
617 binIndex--;
618 }
619 return static_cast<int>(binIndex);
620 }
621 }
622}
623
632size_t indexOfValueFromEdges(std::span<double const> bin_edges, const double value) {
633 if (bin_edges.empty()) {
634 throw std::out_of_range("indexOfValue - vector is empty");
635 }
636 if (bin_edges.size() == 1) {
637 throw std::out_of_range("indexOfValue - requires at least two bin edges");
638 }
639 if (value < bin_edges.front()) {
640 throw std::out_of_range("indexOfValue - value out of range");
641 }
642 const auto it = std::lower_bound(bin_edges.begin(), bin_edges.end(), value);
643 if (it == bin_edges.end()) {
644 throw std::out_of_range("indexOfValue - value out of range");
645 }
646 // index of closest edge above value is distance of iterator from start
647 size_t edgeIndex = std::distance(bin_edges.begin(), it);
648 // if the element n is the first that is >= value, then the value is in (n-1)
649 // th bin
650 if (edgeIndex > 0)
651 edgeIndex--;
652 return edgeIndex;
653}
654
655//-------------------------------------------------------------------------------------------------
660bool isConstantValue(std::span<double const> arra) {
661 // make comparisons with the first value
662 auto i = arra.begin();
663
664 if (i == arra.end()) { // empty array
665 return true;
666 }
667
668 double val(*i);
669 // this loop can be entered! NAN values make comparisons difficult because nan
670 // != nan, deal with these first
671 for (; val != val;) {
672 ++i;
673 if (i == arra.end()) {
674 // all values are contant (NAN)
675 return true;
676 }
677 val = *i;
678 }
679
680 for (; i != arra.end(); ++i) {
681 if (*i != val) {
682 return false;
683 }
684 }
685 // no different value was found and so every must be equal to c
686 return true;
687}
688
689//-------------------------------------------------------------------------------------------------
698template <typename NumT>
699std::vector<NumT> splitStringIntoVector(std::string listString, const std::string &separators) {
700 // Split the string and turn it into a vector.
701 std::vector<NumT> values;
702 std::vector<std::string> strs;
703
704 boost::split(strs, listString, boost::is_any_of(separators));
705 for (auto &str : strs) {
706 if (!str.empty()) {
707 // String not empty
708 std::stringstream oneNumber(str);
709 NumT num;
710 oneNumber >> num;
711 values.emplace_back(num);
712 }
713 }
714 return values;
715}
716
717//-------------------------------------------------------------------------------------------------
726int getBinIndex(std::span<double const> bins, const double value) {
727 assert(bins.size() >= 2);
728 // Since we cast to an int below:
729 assert(bins.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
730 // If X is below the min value
731 if (value < bins.front())
732 return 0;
733
734 // upper_bound will find the right-hand bin boundary (even if the value is
735 // equal to
736 // the left-hand one) - hence we subtract 1 from the found point.
737 // Since we want to return the LH boundary of the last bin if the value is
738 // outside
739 // the upper range, we leave the last value out (i.e. bins.end()-1)
740 auto it = std::upper_bound(bins.begin(), bins.end() - 1, value) - 1;
741 assert(it >= bins.begin());
742 // Convert an iterator to an index for the return value
743 return static_cast<int>(it - bins.begin());
744}
745
746//-------------------------------------------------------------------------------------------------
747
748namespace {
761double runAverage(size_t index, size_t startIndex, size_t endIndex, const double halfWidth,
762 std::span<double const> input, std::span<double const> binBndrs) {
763
764 size_t iStart, iEnd;
765 double weight0(0), weight1(0), start(0.0), end(0.0);
766 //
767 if (!binBndrs.empty()) {
768 // identify initial and final bins to
769 // integrate over. Notice the difference
770 // between start and end bin and shift of
771 // the interpolating function into the center
772 // of each bin
773 auto const &rBndrs = binBndrs;
774 // bin0 = binBndrs[index + 1] - binBndrs[index];
775
776 double binC = 0.5 * (rBndrs[index + 1] + rBndrs[index]);
777 start = binC - halfWidth;
778 end = binC + halfWidth;
779 if (start <= rBndrs[startIndex]) {
780 iStart = startIndex;
781 start = rBndrs[iStart];
782 } else {
783 iStart = getBinIndex(binBndrs, start);
784 weight0 = (rBndrs[iStart + 1] - start) / (rBndrs[iStart + 1] - rBndrs[iStart]);
785 iStart++;
786 }
787 if (end >= rBndrs[endIndex]) {
788 iEnd = endIndex; // the signal defined up to i<iEnd
789 end = rBndrs[endIndex];
790 } else {
791 iEnd = getBinIndex(binBndrs, end);
792 weight1 = (end - rBndrs[iEnd]) / (rBndrs[iEnd + 1] - rBndrs[iEnd]);
793 }
794 if (iStart > iEnd) { // start and end get into the same bin
795 weight1 = 0;
796 weight0 = (end - start) / (rBndrs[iStart] - rBndrs[iStart - 1]);
797 }
798 } else { // integer indexes and functions defined in the bin centers
799 auto iHalfWidth = static_cast<size_t>(halfWidth);
800 iStart = index - iHalfWidth;
801 if (startIndex + iHalfWidth > index)
802 iStart = startIndex;
803 iEnd = index + iHalfWidth;
804 if (iEnd > endIndex)
805 iEnd = endIndex;
806 }
807
808 double avrg = 0;
809 size_t ic = 0;
810 for (size_t j = iStart; j < iEnd; j++) {
811 avrg += input[j];
812 ic++;
813 }
814 if (!binBndrs.empty()) { // add values at edges
815 if (iStart != startIndex)
816 avrg += input[iStart - 1] * weight0;
817 if (iEnd != endIndex)
818 avrg += input[iEnd] * weight1;
819
820 double div = end - start;
821 if (.0 == div)
822 return 0;
823 else
824 return avrg / (end - start);
825 } else {
826 if (0 == ic) {
827 return 0;
828 } else {
829 return avrg / double(ic);
830 }
831 }
832}
833} // namespace
834
859void smoothInRange(std::span<double const> input, std::vector<double> &output, const double avrgInterval,
860 std::span<double const> binBndrs, size_t startIndex, size_t endIndex,
861 std::vector<double> *const outBins) {
862 bool const haveBndrs = !binBndrs.empty();
863
864 if (endIndex == 0)
865 endIndex = input.size();
866 if (endIndex > input.size())
867 endIndex = input.size();
868
869 if (endIndex <= startIndex) {
870 output.resize(0);
871 return;
872 }
873
874 size_t max_size = input.size();
875 if (haveBndrs) {
876 if (binBndrs.size() != max_size + 1) {
877 throw std::invalid_argument("Array of bin boundaries, "
878 "if present, have to be one bigger then the input array");
879 }
880 }
881
882 size_t length = endIndex - startIndex;
883 output.resize(length);
884
885 double halfWidth = avrgInterval / 2;
886 if (!haveBndrs) {
887 if (std::floor(halfWidth) * 2 - avrgInterval > 1.e-6) {
888 halfWidth = std::floor(halfWidth) + 1;
889 }
890 }
891
892 if (outBins)
893 outBins->resize(length + 1);
894
895 // Run averaging
896 double binSize = 1;
897 for (size_t i = startIndex; i < endIndex; i++) {
898 if (haveBndrs) {
899 binSize = binBndrs[i + 1] - binBndrs[i];
900 }
901 output[i - startIndex] = runAverage(i, startIndex, endIndex, halfWidth, input, binBndrs) * binSize;
902 if (outBins && haveBndrs) {
903 outBins->operator[](i - startIndex) = binBndrs[i];
904 }
905 }
906 if (outBins && haveBndrs) {
907 outBins->operator[](endIndex - startIndex) = binBndrs[endIndex];
908 }
909}
910
912template MANTID_KERNEL_DLL std::vector<int32_t> splitStringIntoVector<int32_t>(std::string listString,
913 const std::string &separator);
914template MANTID_KERNEL_DLL std::vector<int64_t> splitStringIntoVector<int64_t>(std::string listString,
915 const std::string &separator);
916template MANTID_KERNEL_DLL std::vector<size_t> splitStringIntoVector<size_t>(std::string listString,
917 const std::string &separator);
918template MANTID_KERNEL_DLL std::vector<float> splitStringIntoVector<float>(std::string listString,
919 const std::string &separator);
920template MANTID_KERNEL_DLL std::vector<double> splitStringIntoVector<double>(std::string listString,
921 const std::string &separator);
922template MANTID_KERNEL_DLL std::vector<std::string> splitStringIntoVector<std::string>(std::string listString,
923 const std::string &separator);
924
925} // namespace Mantid::Kernel::VectorHelper
gsl_vector * tmp
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
size_t iEnd
size_t iStart
MANTID_KERNEL_DLL void smoothInRange(std::span< double const > input, std::vector< double > &output, double avrgInterval, std::span< double const > binBndrs={}, size_t startIndex=0, size_t endIndex=0, std::vector< double > *const outBins=nullptr)
Basic running average of input vector within specified range, considering variable bin-boundaries if ...
void MANTID_KERNEL_DLL rebin(std::span< double const > xold, std::span< double const > yold, std::span< double const > eold, std::span< double const > xnew, std::span< double > ynew, std::span< double > enew, bool distribution, bool addition=false)
The input and output ranges are taken as spans so that the size-checked histogram data types,...
std::size_t MANTID_KERNEL_DLL estimateNumberOfBins(std::vector< double > const &params, double const power=-1)
Returns a size_t with the estimated number of bins that would be needed for a rebinning operation.
int MANTID_KERNEL_DLL indexOfValueFromCentersNoThrow(std::span< double const > bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and returns -1 if out of range.
template MANTID_KERNEL_DLL std::vector< double > splitStringIntoVector< double >(std::string listString, const std::string &separator)
template MANTID_KERNEL_DLL std::vector< int64_t > splitStringIntoVector< int64_t >(std::string listString, const std::string &separator)
template MANTID_KERNEL_DLL std::vector< size_t > splitStringIntoVector< size_t >(std::string listString, const std::string &separator)
MANTID_KERNEL_DLL int getBinIndex(std::span< double const > bins, const double value)
Return the index into a vector of bin boundaries for a particular X value.
void MANTID_KERNEL_DLL rebinHistogram(std::span< double const > xold, std::span< double const > yold, std::span< double const > eold, std::span< double const > xnew, std::span< double > ynew, std::span< double > enew, bool addition)
Rebins histogram data according to a new output X array.
void MANTID_KERNEL_DLL convertToBinBoundary(std::span< double const > bin_centers, std::vector< double > &bin_edges)
Convert an array of bin centers to bin boundary values.
template MANTID_KERNEL_DLL std::vector< int32_t > splitStringIntoVector< int32_t >(std::string listString, const std::string &separator)
Declare all version of this.
template MANTID_KERNEL_DLL std::vector< float > splitStringIntoVector< float >(std::string listString, const std::string &separator)
bool MANTID_KERNEL_DLL isConstantValue(std::span< double const > arra)
Assess if all the values in the vector are equal or if there are some different values.
MANTID_KERNEL_DLL std::vector< NumT > splitStringIntoVector(std::string listString, const std::string &separators=", ")
Take a string of comma or space-separated values, and splits it into a vector of doubles.
template MANTID_KERNEL_DLL std::vector< std::string > splitStringIntoVector< std::string >(std::string listString, const std::string &separator)
void MANTID_KERNEL_DLL convertToBinCentre(std::span< double const > bin_edges, std::vector< double > &bin_centres)
Convert an array of bin boundaries to bin center values.
void MANTID_KERNEL_DLL validateRebinParameters(std::vector< double > const &, bool const =false)
Validate rebinning parameters, throwing an error if any assumptions are invalidated.
size_t MANTID_KERNEL_DLL indexOfValueFromEdges(std::span< double const > bin_edges, const double value)
Gets the bin of a value from a vector of bin edges.
size_t MANTID_KERNEL_DLL indexOfValueFromCenters(std::span< double const > bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and throws exception if out of range.
std::size_t MANTID_KERNEL_DLL createAxisFromRebinParams(const std::vector< double > &params, std::vector< double > &xnew, const bool resize_xnew=true, const bool full_bins_only=false, const double xMinHint=std::nan(""), const double xMaxHint=std::nan(""), const bool useReverseLogarithmic=false, const double power=-1)
Creates a new output X array given a 'standard' set of rebinning parameters.
std::string to_string(const wide_integer< Bits, Signed > &n)
A binary functor to compute the simple average of 2 numbers.