OpenMS
MathFunctions.h
Go to the documentation of this file.
1 // Copyright (c) 2002-present, OpenMS Inc. -- EKU Tuebingen, ETH Zurich, and FU Berlin
2 // SPDX-License-Identifier: BSD-3-Clause
3 //
4 // --------------------------------------------------------------------------
5 // $Maintainer: Timo Sachsenberg$
6 // $Authors: Marc Sturm $
7 // --------------------------------------------------------------------------
8 
9 #pragma once
10 
12 #include <OpenMS/CONCEPT/Macros.h>
13 #include <OpenMS/CONCEPT/Types.h>
15 
16 #include <boost/random/mersenne_twister.hpp> // for mt19937_64
17 #include <boost/random/uniform_int.hpp>
18 #include <cmath>
19 #include <boost/math/special_functions/binomial.hpp>
20 #include <boost/math/special_functions/gamma.hpp>
21 #include <boost/math/special_functions/log1p.hpp>
22 #include <boost/math/distributions/binomial.hpp>
23 #include <boost/math/distributions/complement.hpp>
24 #include <limits>
25 #include <utility> // for std::pair
26 #include <vector>
27 
28 namespace OpenMS
29 {
37 namespace Math
38 {
39 
48  template<typename T>
49  bool extendRange(T& min, T& max, const T& value)
50  {
51  if (value < min)
52  {
53  min = value;
54  return true;
55  }
56  if (value > max)
57  {
58  max = value;
59  return true;
60  }
61  return false;
62  }
63 
69  template<typename T>
70  bool contains(T value, T min, T max)
71  {
72  return min <= value && value <= max;
73  }
74 
95  inline std::pair<double, double> zoomIn(const double left, const double right, const float factor, const float align)
96  {
97  OPENMS_PRECONDITION(factor >= 0, "Factor must be >=0")
98  OPENMS_PRECONDITION(align >= 0, "align must be >=0")
99  OPENMS_PRECONDITION(align <= 1, "align must be <=1")
100  std::pair<double, double> res;
101  auto old_width = right - left;
102  auto offset_left = (1.0f - factor) * old_width * align;
103  res.first = left + offset_left;
104  res.second = res.first + old_width * factor;
105  return res;
106  }
107 
108  using BinContainer = std::vector<RangeBase>;
124  inline BinContainer createBins(double min, double max, uint32_t number_of_bins, double extend_margin = 0)
125  {
126  OPENMS_PRECONDITION(number_of_bins >= 1, "Number of bins must be >= 1")
127  OPENMS_PRECONDITION(min < max, "Require min < max");
128  std::vector<RangeBase> res(number_of_bins);
129  const double bin_width = (max - min) / number_of_bins;
130  for (uint32_t i = 0; i < number_of_bins; ++i)
131  {
132  res[i] = RangeBase(min + i * bin_width, min + (i + 1) * bin_width);
133  res[i].extendLeftRight(extend_margin);
134  }
135  res.front().setMin(min); // undo potential margin
136  res.back().setMax(max); // undo potential margin
137 
138  return res;
139  }
140 
141 
153  inline double ceilDecimal(double x, int decPow)
154  {
155  return (ceil(x / pow(10.0, decPow))) * pow(10.0, decPow); // decimal shift right, ceiling, decimal shift left
156  }
157 
168  inline double roundDecimal(double x, int decPow)
169  {
170  if (x > 0) return (floor(0.5 + x / pow(10.0, decPow))) * pow(10.0, decPow);
171 
172  return -((floor(0.5 + fabs(x) / pow(10.0, decPow))) * pow(10.0, decPow));
173  }
174 
180  inline double intervalTransformation(double x, double left1, double right1, double left2, double right2)
181  {
182  return left2 + (x - left1) * (right2 - left2) / (right1 - left1);
183  }
184 
192  inline double linear2log(double x)
193  {
194  return log10(x + 1); //+1 to avoid negative logarithms
195  }
196 
204  inline double log2linear(double x)
205  {
206  return pow(10, x) - 1;
207  }
208 
214  inline bool isOdd(UInt x)
215  {
216  return (x & 1) != 0;
217  }
218 
224  template<typename T>
225  T round(T x)
226  {
227  return std::round(x);
228  }
229 
244  template<typename T>
245  T roundTo(const T value, int digits)
246  {
247  T factor = 1.0;
248  if (digits > 0)
249  {
250  for (int i = 0; i < digits; ++i)
251  factor *= 10.0;
252  }
253  else if (digits < 0)
254  {
255  for (int i = 0; i < -digits; ++i)
256  factor /= 10.0;
257  }
258 
259  return std::round(value * factor) / factor;
260  }
261 
279  template<typename T>
280  double percentOf(T value, T total, int digits)
281  {
282  if (value < 0) { throw Exception::InvalidValue(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION, "Value must be non-negative", String(value)); }
283  if (total < 0) { throw Exception::InvalidValue(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION, "Total must be non-negative", String(total)); }
284  if (total <= 0) // avoid float equality compare
285  {
286  return 0.0; // avoid division by zero
287  }
288  return roundTo(value * 100.0 / total, digits);
289  }
290 
296  inline bool approximatelyEqual(double a, double b, double tol)
297  {
298  return std::fabs(a - b) <= tol;
299  }
300 
309  template<typename T>
310  T gcd(T a, T b)
311  {
312  T c;
313  while (b != 0)
314  {
315  c = a % b;
316  a = b;
317  b = c;
318  }
319  return a;
320  }
321 
334  template<typename T>
335  T gcd(T a, T b, T& u1, T& u2)
336  {
337  u1 = 1;
338  u2 = 0;
339  T u3 = a;
340 
341  T v1 = 0;
342  T v2 = 1;
343  T v3 = b;
344 
345  while (v3 != 0)
346  {
347  T q = u3 / v3;
348  T t1 = u1 - v1 * q;
349  T t2 = u2 - v2 * q;
350  T t3 = u3 - v3 * q;
351 
352  u1 = v1;
353  u2 = v2;
354  u3 = v3;
355 
356  v1 = t1;
357  v2 = t2;
358  v3 = t3;
359  }
360 
361  return u3;
362  }
363 
373  template<typename T>
374  T getPPM(T mz_obs, T mz_ref)
375  {
376  return (mz_obs - mz_ref) / mz_ref * 1e6;
377  }
378 
388  template<typename T>
389  T getPPMAbs(T mz_obs, T mz_ref)
390  {
391  return std::fabs(getPPM(mz_obs, mz_ref));
392  }
393 
403  template<typename T>
404  T ppmToMass(T ppm, T mz_ref)
405  {
406  return (ppm / T(1e6)) * mz_ref;
407  }
408 
409  /*
410  @brief Compute the absolute mass diff in [Th], given a ppm value and a reference point.
411 
412  The returned mass diff is always positive!
413 
414  @param ppm Parts-per-million error
415  @param mz_ref Reference m/z
416  @return The absolute mass diff in [Th]
417  */
418  template<typename T>
419  T ppmToMassAbs(T ppm, T mz_ref)
420  {
421  return std::fabs(ppmToMass(ppm, mz_ref));
422  }
423 
437  inline std::pair<double, double> getTolWindow(double val, double tol, bool ppm)
438  {
439  double left, right;
440 
441  if (ppm)
442  {
443  left = val - val * tol * 1e-6;
444  right = val / (1.0 - tol * 1e-6);
445  }
446  else
447  {
448  left = val - tol;
449  right = val + tol;
450  }
451 
452  return std::make_pair(left, right);
453  }
454 
458  template<typename T1>
459  typename T1::value_type quantile(const T1& x, double q)
460  {
461  if (x.empty()) throw Exception::InvalidParameter(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION, "Quantile requested from empty container.");
462  if (q < 0.0) q = 0.;
463  if (q > 1.0) q = 1.;
464 
465  const auto n = x.size();
466  const auto id = std::max(0., n * q - 1); // -1 for c++ index starting at 0
467  const auto lo = floor(id);
468  const auto hi = ceil(id);
469  const auto qs = x[lo];
470  const auto h = (id - lo);
471 
472  return (1.0 - h) * qs + h * x[hi];
473  }
474 
475  // portable random shuffle
476  class OPENMS_DLLAPI RandomShuffler
477  {
478  public:
479  explicit RandomShuffler(int seed): rng_(boost::mt19937_64(seed))
480  {
481  }
482 
483  explicit RandomShuffler(const boost::mt19937_64& mt_rng): rng_(mt_rng)
484  {
485  }
486 
487  RandomShuffler() = default;
488  ~RandomShuffler() = default;
489 
490  boost::mt19937_64 rng_;
491  template<class RandomAccessIterator>
492  void portable_random_shuffle(RandomAccessIterator first, RandomAccessIterator last)
493  {
494  for (auto i = (last - first) - 1; i > 0; --i) // OMS_CODING_TEST_EXCLUDE
495  {
496  boost::uniform_int<decltype(i)> d(0, i);
497  std::swap(first[i], first[d(rng_)]);
498  }
499  }
500 
501  void seed(uint64_t val)
502  {
503  rng_.seed(val);
504  }
505  };
506 
515  inline double log_binomial_coef(unsigned n, unsigned k)
516  {
517  // Handle edge cases for improved numerical stability
518  if (k > n)
519  {
520  throw std::invalid_argument("k cannot be greater than n in binomial coefficient");
521  }
522 
523  if (k == 0 || k == n)
524  {
525  return 0.0; // log(1) = 0
526  }
527 
528  // Use symmetry to minimize computation for large k
529  if (k > n / 2)
530  {
531  k = n - k;
532  }
533 
534  return boost::math::lgamma(n + 1.0) - boost::math::lgamma(k + 1.0) - boost::math::lgamma(n - k + 1.0);
535  }
536 
544  inline double log_sum_exp(double x, double y)
545  {
546  // Handle infinite cases
547  if (std::isinf(x) && x < 0) return y;
548  if (std::isinf(y) && y < 0) return x;
549 
550  // Use the maximum value for numerical stability
551  double max_val = std::max(x, y);
552  return max_val + std::log(std::exp(x - max_val) + std::exp(y - max_val));
553  }
554 
567  inline double binomial_cdf_complement(unsigned N, unsigned n, double p)
568  {
569  if (p < 0.0 || p > 1.0)
570  {
571  throw std::invalid_argument("Probability p must be between 0 and 1");
572  }
573  if (n > N)
574  {
575  throw std::invalid_argument("n cannot be greater than N");
576  }
577 
578  if (n == 0) return 1.0; // P(X ≥ 0) = 1
579  if (p == 0.0) return (n == 0) ? 1.0 : 0.0;
580  if (p == 1.0) return 1.0; // all mass at N
581 
582  const boost::math::binomial_distribution<double> dist(N, p);
583  return boost::math::cdf(boost::math::complement(dist, n - 1));
584  }
585 } // namespace Math
586 } // namespace OpenMS
Exception indicating that an invalid parameter was handed over to an algorithm.
Definition: Exception.h:316
Invalid value exception.
Definition: Exception.h:305
Definition: MathFunctions.h:477
RandomShuffler(int seed)
Definition: MathFunctions.h:479
boost::mt19937_64 rng_
Definition: MathFunctions.h:490
void seed(uint64_t val)
Definition: MathFunctions.h:501
RandomShuffler(const boost::mt19937_64 &mt_rng)
Definition: MathFunctions.h:483
void portable_random_shuffle(RandomAccessIterator first, RandomAccessIterator last)
Definition: MathFunctions.h:492
A more convenient string class.
Definition: String.h:34
unsigned int UInt
Unsigned integer type.
Definition: Types.h:64
#define OPENMS_PRECONDITION(condition, message)
Precondition macro.
Definition: openms/include/OpenMS/CONCEPT/Macros.h:94
T gcd(T a, T b)
Returns the greatest common divisor (gcd) of two numbers by applying the Euclidean algorithm.
Definition: MathFunctions.h:310
double intervalTransformation(double x, double left1, double right1, double left2, double right2)
transforms point x of interval [left1,right1] into interval [left2,right2]
Definition: MathFunctions.h:180
double linear2log(double x)
Transforms a number from linear to log10 scale. Avoids negative logarithms by adding 1.
Definition: MathFunctions.h:192
T round(T x)
Rounds the value.
Definition: MathFunctions.h:225
double log2linear(double x)
Transforms a number from log10 to to linear scale. Subtracts the 1 added by linear2log(double)
Definition: MathFunctions.h:204
double ceilDecimal(double x, int decPow)
rounds x up to the next decimal power 10 ^ decPow
Definition: MathFunctions.h:153
bool approximatelyEqual(double a, double b, double tol)
Returns if a is approximately equal b , allowing a tolerance of tol.
Definition: MathFunctions.h:296
double roundDecimal(double x, int decPow)
rounds x to the next decimal power 10 ^ decPow
Definition: MathFunctions.h:168
bool isOdd(UInt x)
Returns true if the given integer is odd.
Definition: MathFunctions.h:214
const double c
Definition: Constants.h:188
const double k
Definition: Constants.h:132
const double h
Definition: Constants.h:141
BinContainer createBins(double min, double max, uint32_t number_of_bins, double extend_margin=0)
Split a range [min,max] into number_of_bins (with optional overlap) and return the ranges of each bin...
Definition: MathFunctions.h:124
bool extendRange(T &min, T &max, const T &value)
Given an interval/range and a new value, extend the range to include the new value if needed.
Definition: MathFunctions.h:49
T getPPM(T mz_obs, T mz_ref)
Compute parts-per-million of two m/z values.
Definition: MathFunctions.h:374
T1::value_type quantile(const T1 &x, double q)
Returns the value of the q th quantile (0-1) in a sorted non-empty vector x.
Definition: MathFunctions.h:459
T roundTo(const T value, int digits)
Definition: MathFunctions.h:245
T ppmToMass(T ppm, T mz_ref)
Compute the mass diff in [Th], given a ppm value and a reference point.
Definition: MathFunctions.h:404
double binomial_cdf_complement(unsigned N, unsigned n, double p)
Calculate binomial cumulative distribution function P(X ≥ n)
Definition: MathFunctions.h:567
double log_binomial_coef(unsigned n, unsigned k)
Calculate logarithm of binomial coefficient C(n,k) using log-gamma function.
Definition: MathFunctions.h:515
std::pair< double, double > zoomIn(const double left, const double right, const float factor, const float align)
Zoom into an interval [left, right], decreasing its width by factor (which must be in [0,...
Definition: MathFunctions.h:95
double log_sum_exp(double x, double y)
Log-sum-exp operation for numerical stability.
Definition: MathFunctions.h:544
bool contains(T value, T min, T max)
Is a value contained in [min, max] ?
Definition: MathFunctions.h:70
T getPPMAbs(T mz_obs, T mz_ref)
Compute absolute parts-per-million of two m/z values.
Definition: MathFunctions.h:389
T ppmToMassAbs(T ppm, T mz_ref)
Definition: MathFunctions.h:419
double percentOf(T value, T total, int digits)
Definition: MathFunctions.h:280
std::pair< double, double > getTolWindow(double val, double tol, bool ppm)
Return tolerance window around val given tolerance tol.
Definition: MathFunctions.h:437
std::vector< RangeBase > BinContainer
Definition: MathFunctions.h:108
Main OpenMS namespace.
Definition: openswathalgo/include/OpenMS/OPENSWATHALGO/DATAACCESS/ISpectrumAccess.h:19
Base class for a simple range with minimum and maximum.
Definition: RangeManager.h:37