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/Memory.h"
12#include <algorithm>
13#include <boost/algorithm/string.hpp>
14#include <numeric>
15#include <sstream>
16
17using std::size_t;
18
19namespace {
20// edge checks
22struct EdgeCheck { // for polymorphic access to functors
24 virtual bool operator()(double, double, double) const = 0;
25 virtual ~EdgeCheck() = default;
26};
27
28// Standard check, for linear, log, and power
29struct StandardEdgeCheck : EdgeCheck {
30 StandardEdgeCheck(double lastBinCoef) : m_lastBinCoef(lastBinCoef) {}
31 bool operator()(double xcurr, double xs, double rightEdge) const override {
32 return xcurr + (1. + m_lastBinCoef) * xs <= rightEdge;
33 }
34
35private:
36 double m_lastBinCoef;
37};
38// Reverse log check, for reverse log only
39struct ReverseLogEdgeCheck : EdgeCheck {
40 bool operator()(double xcurr, double xs, double leftEdge) const override { return xcurr + 2. * xs >= leftEdge; }
41};
42
43// bin width functions
45struct BinWidth { // for polymorphic access to functors
47 virtual double operator()(double, double) const = 0;
48 virtual ~BinWidth() = default;
49};
50
51// Linear bin width is just the alpha parameter
52struct LinearBinWidth : BinWidth {
53 double operator()([[maybe_unused]] double xcurr, double alpha) const override { return alpha; }
54};
55// Log bin width is current position times the alpha parameter
56struct LogBinWidth : BinWidth {
57 double operator()(double xcurr, double alpha) const override { return xcurr * alpha; }
58};
59
60// REVERSE LOGARITHMIC
61// we go through a reverse log bin by starting from its end, and working our way back to the beginning
62// this way we can define the bins in a recurring way, and with a more obvious closeness with the usual log.
63struct ReverseLogBinWidth : BinWidth {
64 ReverseLogBinWidth(double leftEdge, double rightEdge) : m_leftEdge(leftEdge), m_rightEdge(rightEdge) {}
65 double operator()(double xcurr, double alpha) const override { return (xcurr - m_rightEdge - m_leftEdge) * alpha; }
66
67private:
68 double m_leftEdge;
69 double m_rightEdge;
70};
71// Power bin width is the alpha parameter divided by the current index to the power
72struct PowerBinWidth : BinWidth {
73 PowerBinWidth(std::size_t &currDiv, double power) : m_currDiv(&currDiv), m_power(power) {}
74 double operator()([[maybe_unused]] double xcurr, double alpha) const override {
75 return alpha * std::pow((*m_currDiv)++, -m_power);
76 }
77
78private:
79 std::size_t *const m_currDiv; // the pointed-to changes, but not the pointer
80 double m_power;
81};
82} // namespace
83
85
87void validateRebinParameters(std::vector<double> const &params, bool const isPower) {
88 size_t numParams = params.size();
89 if (numParams < 3 || numParams % 2 != 1) {
90 throw std::runtime_error("validateRebinParameters: Invalid number of rebin parameters (" +
91 std::to_string(numParams) + ")! Must be odd number of parameters, 3 or more.");
92 }
93 double previousEdge = params[0];
94 for (size_t i = 1; i < numParams - 1; i += 2) {
95 double const binWidth = params[i];
96 double const nextEdge = params[i + 1];
97 if (previousEdge >= nextEdge) {
98 throw std::runtime_error(
99 "validateRebinParameters: Bin boundary values must be given in order of increasing value! " +
100 std::to_string(previousEdge) + " >= " + std::to_string(nextEdge));
101 } else if (binWidth == 0) {
102 throw std::runtime_error("validateRebinParameters: Bin width cannot be zero! Found in range " +
103 std::to_string(previousEdge) + " to " + std::to_string(nextEdge));
104 } else if (binWidth < 0 && previousEdge <= 0) {
105 throw std::runtime_error("Bin boundaries must be positive for logarithmic binning. Got " +
106 std::to_string(previousEdge) + " to " + std::to_string(nextEdge));
107 } else if (binWidth < 0 && isPower) {
108 throw std::runtime_error(
109 "validateRebinParameters: Cannot use power binning with negative bin width! Bin width was " +
110 std::to_string(binWidth));
111 }
112 previousEdge = nextEdge;
113 }
114}
115
120std::size_t estimateNumberOfBins(std::vector<double> const &params, double const power) {
121 bool const isPower = power > 0 && power <= 1;
122 validateRebinParameters(params, isPower);
123 double numBins = 1.; // always start with 1 left edge / fencepost
124 double previousEdge = params[0];
125 for (size_t i = 1; i < params.size() - 1; i += 2) {
126 double const binWidth = params[i];
127 double const nextEdge = params[i + 1];
128 if (binWidth < 0) {
129 // LOGARITHMIC BINNING
130 // NOTE log or reverse log should have approximately the same number of bins here
131 numBins += std::ceil(std::log(nextEdge / previousEdge) / std::log(1. - binWidth));
132 } else if (isPower) {
133 // POWER BINNING
134 // formulas taken from Rebin::validateInputs
135 if (power == 1) {
136 double constexpr eulerMascheroni = 0.5772156649; // Euler-Mascheroni constant
137 numBins += std::exp((nextEdge - previousEdge) / binWidth - eulerMascheroni);
138 } else {
139 numBins += std::pow(((nextEdge - previousEdge) / binWidth) * (1 - power) + 1, 1 / (1 - power));
140 }
141 } else if (binWidth > 0) {
142 // LINEAR BINNING
143 numBins += std::ceil((nextEdge - previousEdge) / binWidth);
144 }
145 previousEdge = nextEdge;
146 }
147 if (numBins >= static_cast<double>(std::numeric_limits<std::size_t>::max())) {
148 throw std::runtime_error(
149 "estimateNumberOfBins: The number of bins requested exceeds the maximum storable by size_t.");
150 }
151 return static_cast<size_t>(numBins);
152}
153
168std::size_t createAxisFromRebinParams(const std::vector<double> &params, std::vector<double> &xnew,
169 const bool resize_xnew, const bool full_bins_only, const double xMinHint,
170 const double xMaxHint, const bool useReverseLogarithmic, const double power) {
171
172 // correctly expand the parameters
173 std::vector<double> tmp;
174 if (params.size() == 1) {
175 if (std::isnan(xMinHint) || std::isnan(xMaxHint)) {
176 throw std::runtime_error(
177 "createAxisFromRebinParams: xMinHint and xMaxHint must be supplied if params contains only the bin width.");
178 }
179 tmp = {xMinHint, params.front(), xMaxHint};
180 }
181 std::vector<double> const &fullParams = params.size() == 1 ? tmp : params;
182
183 // This coefficient represents the maximum difference between the size of the last bin and all
184 // the other bins.
185 // For full_bin_only, we want it so that last bin couldn't be smaller than the previous bin
186 double const lastBinCoef = full_bins_only ? 1.0 : 0.25;
187 // whether to use power binning
188 bool isPower = power > 0 && power <= 1;
189
190 // if resize_xnew is true, prepare the vector
191 double xcurr = fullParams[0];
192 xnew.clear();
193 if (resize_xnew) {
194 // call to estimateNumberOfBins will also validate the parameters
195 size_t neededSize = estimateNumberOfBins(fullParams, power);
196 /* detect potential memory overflows
197 * TODO find a way to check against actual available system memory
198 * NOTE use of MemoryStats.availMem() was considered, but introduces large overhead into this hot path
199 */
200 if (neededSize >= xnew.max_size()) {
201 throw std::runtime_error(
202 "createAxisFromRebinParams: The number of bins requested exceeds the maximum storable by the output vector.");
203 }
204 xnew.reserve(neededSize);
205 xnew.push_back(xcurr);
206 } else {
207 // validate the parameters, but don't calculate needed size
208 validateRebinParameters(fullParams, isPower);
209 }
210
211 std::size_t currDiv = 1;
212
213 // for each range specified, fill in the axis with correct bin edges
214 std::size_t inew = 1; // we start with 1, the leftmost edge
215 double leftEdge = fullParams[0];
216 for (std::size_t i = 1; i <= fullParams.size() - 1; i += 2) {
217 double binParam = fullParams[i];
218 double rightEdge = fullParams[i + 1];
219 double alpha = std::fabs(binParam);
220
221 // functions for calculating bin width and checking if within range
222 std::unique_ptr<BinWidth> binWidth(nullptr);
223 std::unique_ptr<EdgeCheck> edgeCheck(nullptr);
224 if (binParam < 0.0 && useReverseLogarithmic) {
225 // REVERSE LOGARITHMIC
226 binWidth = std::make_unique<ReverseLogBinWidth>(leftEdge, rightEdge);
227 edgeCheck = std::make_unique<ReverseLogEdgeCheck>();
228 // for reverse log, we start at the right edge and work back to left; to use the same loop, swap the edges
229 // the final edge will be added after the loop, fullParams[i+1]
230 xcurr = rightEdge;
231 std::swap(leftEdge, rightEdge);
232 } else if (binParam < 0.0) {
233 // LOGARITHMIC
234 binWidth = std::make_unique<LogBinWidth>();
235 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
236 } else if (isPower) {
237 // POWER
238 binWidth = std::make_unique<PowerBinWidth>(currDiv, power);
239 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
240 } else {
241 // LINEAR
242 binWidth = std::make_unique<LinearBinWidth>();
243 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
244 }
245
246 double xs = (*binWidth)(xcurr, alpha);
247 while ((*edgeCheck)(xcurr, xs, rightEdge) || !std::isfinite(xs)) { // let errors fall through
248 if (xs == 0.0) {
249 // Someone gave a 0-sized step! What a dope.
250 throw std::runtime_error("Invalid binning step provided! Can't create binning axis.");
251 } else if (!std::isfinite(xs)) {
252 throw std::runtime_error("An infinite or NaN value was found in the binning parameters.");
253 }
254 xcurr = xcurr + xs;
255 if (resize_xnew) {
256 xnew.push_back(xcurr);
257 }
258 inew++;
259 // prepare for next step
260 xs = (*binWidth)(xcurr, alpha);
261 }
262
263 // handle last edge specially, depending on if full_bins_only is set
264 if (!useReverseLogarithmic && full_bins_only) {
265 // For full_bins_only, we want to add one more full bin, so that last bin is not bigger than the previous one
266 xcurr = xcurr + xs;
267 } else {
268 // For non full_bins_only, finish by adding as much as is left from the range
269 // NOTE for reverse log, the left edge was already added, so we can instead jump to the right edge of this range
270 xcurr = fullParams[i + 1];
271 }
272 if (resize_xnew) {
273 xnew.push_back(xcurr);
274 }
275 inew++;
276 // prepare for next range
277 leftEdge = xcurr;
278 }
279 // if using reverse log, some bins were added in wrong order; so sort them
280 if (useReverseLogarithmic) {
281 std::sort(xnew.begin(), xnew.end());
282 }
283 return inew;
284}
285
305void rebin(const std::vector<double> &xold, const std::vector<double> &yold, const std::vector<double> &eold,
306 const std::vector<double> &xnew, std::vector<double> &ynew, std::vector<double> &enew, bool distribution,
307 bool addition) {
308 // Make sure y and e vectors are of correct sizes
309 const size_t size_xold = xold.size();
310 if (size_xold != (yold.size() + 1) || size_xold != (eold.size() + 1))
311 throw std::runtime_error("rebin: old y and error vectors should be of same "
312 "size & 1 shorter than x");
313 const size_t size_xnew = xnew.size();
314 if (size_xnew != (ynew.size() + 1) || size_xnew != (enew.size() + 1))
315 throw std::runtime_error("rebin: new y and error vectors should be of same "
316 "size & 1 shorter than x");
317
318 size_t size_yold = yold.size();
319 size_t size_ynew = ynew.size();
320
321 if (!addition) {
322 // Make sure ynew & enew contain zeroes
323 std::fill(ynew.begin(), ynew.end(), 0.0);
324 std::fill(enew.begin(), enew.end(), 0.0);
325 }
326
327 size_t iold = 0, inew = 0;
328 double width;
329
330 while ((inew < size_ynew) && (iold < size_yold)) {
331 double xo_low = xold[iold];
332 double xo_high = xold[iold + 1];
333 double xn_low = xnew[inew];
334 double xn_high = xnew[inew + 1];
335 if (xn_high <= xo_low)
336 inew++; /* old and new bins do not overlap */
337 else if (xo_high <= xn_low)
338 iold++; /* old and new bins do not overlap */
339 else {
340 // delta is the overlap of the bins on the x axis
341 // delta = std::min(xo_high, xn_high) - std::max(xo_low, xn_low);
342 double delta = xo_high < xn_high ? xo_high : xn_high;
343 delta -= xo_low > xn_low ? xo_low : xn_low;
344 width = xo_high - xo_low;
345 if ((delta <= 0.0) || (width <= 0.0)) {
346 // No need to throw here, just return (ynew & enew will be empty)
347 // throw std::runtime_error("rebin: no bin overlap detected");
348 return;
349 }
350 /*
351 * yoldp contains counts/unit time, ynew contains counts
352 * enew contains counts**2
353 * ynew has been filled with zeros on creation
354 */
355 if (distribution) {
356 // yold/eold data is distribution
357 ynew[inew] += yold[iold] * delta;
358 // this error is calculated in the same way as opengenie
359 enew[inew] += eold[iold] * eold[iold] * delta * width;
360 } else {
361 // yold/eold data is not distribution
362 // do implicit division of yold by width in summing.... avoiding the
363 // need for temporary yold array
364 // this method is ~7% faster and uses less memory
365 ynew[inew] += yold[iold] * delta / width; // yold=yold/width
366 // eold=eold/width, so divide by width**2 compared with distribution
367 // calculation
368 enew[inew] += eold[iold] * eold[iold] * delta / width;
369 }
370 if (xn_high > xo_high) {
371 iold++;
372 } else {
373 inew++;
374 }
375 }
376 }
377
378 if (!addition) // If using the addition facility, have to do bin width and
379 // sqrt errors externally
380 {
381 if (distribution) {
382 /*
383 * convert back to counts/unit time
384 */
385 for (size_t i = 0; i < size_ynew; ++i) {
386 {
387 width = xnew[i + 1] - xnew[i];
388 if (width != 0.0) {
389 ynew[i] /= width;
390 enew[i] = sqrt(enew[i]) / width;
391 } else {
392 throw std::invalid_argument("rebin: Invalid output X array, contains consecutive X values");
393 }
394 }
395 }
396 } else {
397 // non-distribution, just square root final error value
398 using pf = double (*)(double);
399 pf uf = std::sqrt;
400 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
401 }
402 }
403}
404
405//-------------------------------------------------------------------------------------------------
422void rebinHistogram(const std::vector<double> &xold, const std::vector<double> &yold, const std::vector<double> &eold,
423 const std::vector<double> &xnew, std::vector<double> &ynew, std::vector<double> &enew,
424 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 ynew.assign(size_ynew, 0.0);
436 enew.assign(size_ynew, 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.cbegin(), xold.cend(), 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.cbegin(), xnew.cend(), xold.front());
450 if (it == xnew.cend())
451 return;
452 // throw std::runtime_error("No overlap: max of X-new < min of X-old");
453 inew = std::distance(xnew.cbegin(), 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(const std::vector<double> &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(const std::vector<double> &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(const std::vector<double> &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(const std::vector<double> &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(const std::vector<double> &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(const std::vector<double> &arra) {
661 // make comparisons with the first value
662 auto i = arra.cbegin();
663
664 if (i == arra.cend()) { // 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.cend()) {
674 // all values are contant (NAN)
675 return true;
676 }
677 val = *i;
678 }
679
680 for (; i != arra.cend(); ++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(const std::vector<double> &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 const std::vector<double> &input, std::vector<double> const *const binBndrs) {
763
764 size_t iStart, iEnd;
765 double weight0(0), weight1(0), start(0.0), end(0.0);
766 //
767 if (binBndrs) {
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 &rBndrs = *binBndrs;
774 // bin0 = binBndrs->operator[](index + 1) - binBndrs->operator[](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) { // 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(const std::vector<double> &input, std::vector<double> &output, const double avrgInterval,
860 std::vector<double> const *const binBndrs, size_t startIndex, size_t endIndex,
861 std::vector<double> *const outBins) {
862
863 if (endIndex == 0)
864 endIndex = input.size();
865 if (endIndex > input.size())
866 endIndex = input.size();
867
868 if (endIndex <= startIndex) {
869 output.resize(0);
870 return;
871 }
872
873 size_t max_size = input.size();
874 if (binBndrs) {
875 if (binBndrs->size() != max_size + 1) {
876 throw std::invalid_argument("Array of bin boundaries, "
877 "if present, have to be one bigger then the input array");
878 }
879 }
880
881 size_t length = endIndex - startIndex;
882 output.resize(length);
883
884 double halfWidth = avrgInterval / 2;
885 if (!binBndrs) {
886 if (std::floor(halfWidth) * 2 - avrgInterval > 1.e-6) {
887 halfWidth = std::floor(halfWidth) + 1;
888 }
889 }
890
891 if (outBins)
892 outBins->resize(length + 1);
893
894 // Run averaging
895 double binSize = 1;
896 for (size_t i = startIndex; i < endIndex; i++) {
897 if (binBndrs) {
898 binSize = binBndrs->operator[](i + 1) - binBndrs->operator[](i);
899 }
900 output[i - startIndex] = runAverage(i, startIndex, endIndex, halfWidth, input, binBndrs) * binSize;
901 if (outBins && binBndrs) {
902 outBins->operator[](i - startIndex) = binBndrs->operator[](i);
903 }
904 }
905 if (outBins && binBndrs) {
906 outBins->operator[](endIndex - startIndex) = binBndrs->operator[](endIndex);
907 }
908}
909
911template DLLExport std::vector<int32_t> splitStringIntoVector<int32_t>(std::string listString,
912 const std::string &separator);
913template DLLExport std::vector<int64_t> splitStringIntoVector<int64_t>(std::string listString,
914 const std::string &separator);
915template DLLExport std::vector<size_t> splitStringIntoVector<size_t>(std::string listString,
916 const std::string &separator);
917template DLLExport std::vector<float> splitStringIntoVector<float>(std::string listString,
918 const std::string &separator);
919template DLLExport std::vector<double> splitStringIntoVector<double>(std::string listString,
920 const std::string &separator);
921template DLLExport std::vector<std::string> splitStringIntoVector<std::string>(std::string listString,
922 const std::string &separator);
923
924} // 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
#define DLLExport
Definitions of the DLLImport compiler directives for MSVC.
Definition System.h:33
size_t iEnd
size_t iStart
MANTID_KERNEL_DLL void smoothInRange(const std::vector< double > &input, std::vector< double > &output, double avrgInterval, std::vector< double > const *const binBndrs=nullptr, 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 ...
template DLLExport std::vector< int64_t > splitStringIntoVector< int64_t >(std::string listString, const std::string &separator)
size_t MANTID_KERNEL_DLL indexOfValueFromCenters(const std::vector< double > &bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and throws exception if out of range.
void MANTID_KERNEL_DLL convertToBinCentre(const std::vector< double > &bin_edges, std::vector< double > &bin_centres)
Convert an array of bin boundaries to bin center values.
template DLLExport std::vector< std::string > splitStringIntoVector< std::string >(std::string listString, const std::string &separator)
template DLLExport std::vector< size_t > splitStringIntoVector< size_t >(std::string listString, const std::string &separator)
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.
bool MANTID_KERNEL_DLL isConstantValue(const std::vector< double > &arra)
Assess if all the values in the vector are equal or if there are some different values.
template DLLExport std::vector< float > splitStringIntoVector< float >(std::string listString, const std::string &separator)
MANTID_KERNEL_DLL int getBinIndex(const std::vector< double > &bins, const double value)
Return the index into a vector of bin boundaries for a particular X value.
template DLLExport std::vector< double > splitStringIntoVector< double >(std::string listString, const std::string &separator)
size_t MANTID_KERNEL_DLL indexOfValueFromEdges(const std::vector< double > &bin_edges, const double value)
Gets the bin of a value from a vector of bin edges.
template DLLExport std::vector< int32_t > splitStringIntoVector< int32_t >(std::string listString, const std::string &separator)
Declare all version of this.
void MANTID_KERNEL_DLL rebin(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool distribution, bool addition=false)
Rebins data according to a new output X array.
void MANTID_KERNEL_DLL rebinHistogram(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool addition)
Rebins histogram data according to a new output X array.
void MANTID_KERNEL_DLL convertToBinBoundary(const std::vector< double > &bin_centers, std::vector< double > &bin_edges)
Convert an array of bin centers to bin boundary 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.
int MANTID_KERNEL_DLL indexOfValueFromCentersNoThrow(const std::vector< double > &bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and returns -1 if out of range.
void MANTID_KERNEL_DLL validateRebinParameters(std::vector< double > const &, bool const =false)
Validate rebinning parameters, throwing an error if any assumptions are invalidated.
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.