10#include "MantidKernel/DllConfig.h"
14#include <boost/algorithm/string.hpp>
25 virtual bool operator()(
double,
double,
double)
const = 0;
26 virtual ~EdgeCheck() =
default;
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;
40struct ReverseLogEdgeCheck : EdgeCheck {
41 bool operator()(
double xcurr,
double xs,
double leftEdge)
const override {
return xcurr + 2. * xs >= leftEdge; }
48 virtual double operator()(
double,
double)
const = 0;
49 virtual ~BinWidth() =
default;
53struct LinearBinWidth : BinWidth {
54 double operator()([[maybe_unused]]
double xcurr,
double alpha)
const override {
return alpha; }
57struct LogBinWidth : BinWidth {
58 double operator()(
double xcurr,
double alpha)
const override {
return xcurr * alpha; }
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; }
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);
80 std::size_t *
const m_currDiv;
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.");
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! " +
102 }
else if (binWidth == 0) {
103 throw std::runtime_error(
"validateRebinParameters: Bin width cannot be zero! Found in range " +
105 }
else if (binWidth < 0 && previousEdge <= 0) {
106 throw std::runtime_error(
"Bin boundaries must be positive for logarithmic binning. Got " +
108 }
else if (binWidth < 0 && isPower) {
109 throw std::runtime_error(
110 "validateRebinParameters: Cannot use power binning with negative bin width! Bin width was " +
113 previousEdge = nextEdge;
122 bool const isPower = power > 0 && power <= 1;
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];
132 numBins += std::ceil(std::log(nextEdge / previousEdge) / std::log(1. - binWidth));
133 }
else if (isPower) {
137 double constexpr eulerMascheroni = 0.5772156649;
138 numBins += std::exp((nextEdge - previousEdge) / binWidth - eulerMascheroni);
140 numBins += std::pow(((nextEdge - previousEdge) / binWidth) * (1 - power) + 1, 1 / (1 - power));
142 }
else if (binWidth > 0) {
144 numBins += std::ceil((nextEdge - previousEdge) / binWidth);
146 previousEdge = nextEdge;
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.");
152 return static_cast<size_t>(numBins);
170 const bool resize_xnew,
const bool full_bins_only,
const double xMinHint,
171 const double xMaxHint,
const bool useReverseLogarithmic,
const double power) {
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.");
180 tmp = {xMinHint, params.front(), xMaxHint};
182 std::vector<double>
const &fullParams = params.size() == 1 ?
tmp : params;
187 double const lastBinCoef = full_bins_only ? 1.0 : 0.25;
189 bool isPower = power > 0 && power <= 1;
192 double xcurr = fullParams[0];
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.");
205 xnew.reserve(neededSize);
206 xnew.push_back(xcurr);
212 std::size_t currDiv = 1;
215 std::size_t inew = 1;
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);
223 std::unique_ptr<BinWidth> binWidth(
nullptr);
224 std::unique_ptr<EdgeCheck> edgeCheck(
nullptr);
225 if (binParam < 0.0 && useReverseLogarithmic) {
227 binWidth = std::make_unique<ReverseLogBinWidth>(leftEdge, rightEdge);
228 edgeCheck = std::make_unique<ReverseLogEdgeCheck>();
232 std::swap(leftEdge, rightEdge);
233 }
else if (binParam < 0.0) {
235 binWidth = std::make_unique<LogBinWidth>();
236 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
237 }
else if (isPower) {
239 binWidth = std::make_unique<PowerBinWidth>(currDiv, power);
240 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
243 binWidth = std::make_unique<LinearBinWidth>();
244 edgeCheck = std::make_unique<StandardEdgeCheck>(lastBinCoef);
247 double xs = (*binWidth)(xcurr, alpha);
248 while ((*edgeCheck)(xcurr, xs, rightEdge) || !std::isfinite(xs)) {
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.");
257 xnew.push_back(xcurr);
261 xs = (*binWidth)(xcurr, alpha);
265 if (!useReverseLogarithmic && full_bins_only) {
271 xcurr = fullParams[i + 1];
274 xnew.push_back(xcurr);
281 if (useReverseLogarithmic) {
282 std::sort(xnew.begin(), xnew.end());
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,
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");
319 size_t size_yold = yold.size();
320 size_t size_ynew = ynew.size();
324 std::fill(ynew.begin(), ynew.end(), 0.0);
325 std::fill(enew.begin(), enew.end(), 0.0);
328 size_t iold = 0, inew = 0;
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)
338 else if (xo_high <= 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)) {
358 ynew[inew] += yold[iold] *
delta;
360 enew[inew] += eold[iold] * eold[iold] *
delta * width;
366 ynew[inew] += yold[iold] *
delta / width;
369 enew[inew] += eold[iold] * eold[iold] *
delta / width;
371 if (xn_high > xo_high) {
386 for (
size_t i = 0; i < size_ynew; ++i) {
388 width = xnew[i + 1] - xnew[i];
391 enew[i] = sqrt(enew[i]) / width;
393 throw std::invalid_argument(
"rebin: Invalid output X array, contains consecutive X values");
399 using pf = double (*)(double);
401 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
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) {
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");
435 std::fill(ynew.begin(), ynew.end(), 0.0);
436 std::fill(enew.begin(), enew.end(), 0.0);
440 size_t iold = 0, inew = 0;
442 if (xnew.front() > xold.front()) {
443 auto it = std::upper_bound(xold.begin(), xold.end(), xnew.front());
444 if (it == xold.end())
447 iold = std::distance(xold.begin(), it) - 1;
449 auto it = std::upper_bound(xnew.begin(), xnew.end(), xold.front());
450 if (it == xnew.end())
453 inew = std::distance(xnew.begin(), it) - 1;
457 double oneOverWidth, overlap;
461 for (; iold < size_yold; ++iold) {
462 double xold_of_iold_p_1 = xold[iold + 1];
464 if (xold_of_iold_p_1 <= xnew[inew + 1]) {
465 ynew[inew] += yold[iold];
467 enew[inew] += temp * temp;
469 if (xold_of_iold_p_1 == xnew[inew + 1])
472 double xold_of_iold = xold[iold];
474 oneOverWidth = 1. / (xold_of_iold_p_1 - xold_of_iold);
475 frac = yold[iold] * oneOverWidth;
477 fracE = temp * temp * oneOverWidth;
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;
488 if (inew == size_ynew)
492 overlap = xold_of_iold_p_1 - xnew[inew];
493 ynew[inew] += frac * overlap;
494 enew[inew] += fracE * overlap;
502 using pf = double (*)(double);
504 std::transform(enew.begin(), enew.end(), enew.begin(), uf);
515 const std::vector<double>::size_type npoints = bin_edges.size();
516 if (bin_centres.size() != npoints) {
517 bin_centres.resize(npoints);
523 std::adjacent_difference(bin_edges.begin(), bin_edges.end(), bin_centres.begin(),
SimpleAverage<double>());
527 bin_centres.erase(bin_centres.begin());
547 const auto n = bin_centers.size();
555 bin_edges.resize(
n + 1);
560 bin_edges[0] = bin_centers[0] - 0.5;
561 bin_edges[1] = bin_centers[0] + 0.5;
565 for (
size_t i = 0; i <
n - 1; ++i) {
566 bin_edges[i + 1] = 0.5 * (bin_centers[i] + bin_centers[i + 1]);
569 bin_edges[0] = bin_centers[0] - (bin_edges[1] - bin_centers[0]);
571 bin_edges[
n] = bin_centers[
n - 1] + (bin_centers[
n - 1] - bin_edges[
n - 1]);
587 return static_cast<size_t>(
index);
589 throw std::out_of_range(
"indexOfValue - value out of range");
593 if (bin_centers.empty()) {
594 throw std::out_of_range(
"indexOfValue - vector is empty");
596 if (bin_centers.size() == 1) {
598 if (
value < bin_centers[0] - 0.5 ||
value > bin_centers[0] + 0.5) {
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) {
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);
614 size_t binIndex = std::distance(bin_centers.begin(), it);
616 value < bin_centers[binIndex - 1] + 0.5 * (bin_centers[binIndex] - bin_centers[binIndex - 1])) {
619 return static_cast<int>(binIndex);
633 if (bin_edges.empty()) {
634 throw std::out_of_range(
"indexOfValue - vector is empty");
636 if (bin_edges.size() == 1) {
637 throw std::out_of_range(
"indexOfValue - requires at least two bin edges");
639 if (
value < bin_edges.front()) {
640 throw std::out_of_range(
"indexOfValue - value out of range");
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");
647 size_t edgeIndex = std::distance(bin_edges.begin(), it);
662 auto i = arra.begin();
664 if (i == arra.end()) {
671 for (; val != val;) {
673 if (i == arra.end()) {
680 for (; i != arra.end(); ++i) {
698template <
typename NumT>
701 std::vector<NumT> values;
702 std::vector<std::string> strs;
704 boost::split(strs, listString, boost::is_any_of(separators));
705 for (
auto &str : strs) {
708 std::stringstream oneNumber(str);
711 values.emplace_back(num);
727 assert(bins.size() >= 2);
729 assert(bins.size() <
static_cast<size_t>(std::numeric_limits<int>::max()));
731 if (
value < bins.front())
740 auto it = std::upper_bound(bins.begin(), bins.end() - 1,
value) - 1;
741 assert(it >= bins.begin());
743 return static_cast<int>(it - bins.begin());
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) {
765 double weight0(0), weight1(0), start(0.0), end(0.0);
767 if (!binBndrs.empty()) {
773 auto const &rBndrs = binBndrs;
776 double binC = 0.5 * (rBndrs[
index + 1] + rBndrs[
index]);
777 start = binC - halfWidth;
778 end = binC + halfWidth;
779 if (start <= rBndrs[startIndex]) {
787 if (end >= rBndrs[endIndex]) {
789 end = rBndrs[endIndex];
792 weight1 = (end - rBndrs[
iEnd]) / (rBndrs[
iEnd + 1] - rBndrs[
iEnd]);
796 weight0 = (end - start) / (rBndrs[
iStart] - rBndrs[
iStart - 1]);
799 auto iHalfWidth =
static_cast<size_t>(halfWidth);
801 if (startIndex + iHalfWidth >
index)
814 if (!binBndrs.empty()) {
816 avrg += input[
iStart - 1] * weight0;
817 if (
iEnd != endIndex)
818 avrg += input[
iEnd] * weight1;
820 double div = end - start;
824 return avrg / (end - start);
829 return avrg / double(ic);
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();
865 endIndex = input.size();
866 if (endIndex > input.size())
867 endIndex = input.size();
869 if (endIndex <= startIndex) {
874 size_t max_size = input.size();
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");
882 size_t length = endIndex - startIndex;
883 output.resize(length);
885 double halfWidth = avrgInterval / 2;
887 if (std::floor(halfWidth) * 2 - avrgInterval > 1.e-6) {
888 halfWidth = std::floor(halfWidth) + 1;
893 outBins->resize(length + 1);
897 for (
size_t i = startIndex; i < endIndex; i++) {
899 binSize = binBndrs[i + 1] - binBndrs[i];
901 output[i - startIndex] = runAverage(i, startIndex, endIndex, halfWidth, input, binBndrs) * binSize;
902 if (outBins && haveBndrs) {
903 outBins->operator[](i - startIndex) = binBndrs[i];
906 if (outBins && haveBndrs) {
907 outBins->operator[](endIndex - startIndex) = binBndrs[endIndex];
913 const std::string &separator);
915 const std::string &separator);
917 const std::string &separator);
919 const std::string &separator);
921 const std::string &separator);
923 const std::string &separator);
double value
The value of the point.
std::map< DeltaEMode::Type, std::string > index
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 ¶ms, 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 > ¶ms, 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.