LCOV - code coverage report
Current view: top level - core/include/scipp/core - value_and_variance.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 68 127 53.5 %
Date: 2024-04-28 01:25:40 Functions: 46 316 14.6 %

          Line data    Source code
       1             : // SPDX-License-Identifier: BSD-3-Clause
       2             : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
       3             : /// @file
       4             : #pragma once
       5             : 
       6             : #include <cmath>
       7             : 
       8             : #include "scipp/common/numeric.h"
       9             : #include "scipp/common/span.h"
      10             : #include "scipp/core/dtype.h"
      11             : 
      12             : namespace scipp::core {
      13             : 
      14             : /// A value/variance pair with operators that propagate uncertainties.
      15             : ///
      16             : /// This is intended for small T such as double, float, and int. It is the
      17             : /// central implementation of uncertainty propagation in scipp, for built-in
      18             : /// operations as well as custom operations using one of the transform
      19             : /// functions. Since T is assumed to be small it is copied into the class and
      20             : /// extracted later. See also ValuesAndVariances.
      21             : template <class T> struct ValueAndVariance {
      22             :   using value_type = std::remove_cv_t<T>;
      23             : 
      24             :   /// This constructor is essential to prevent warnings about narrowing the
      25             :   /// types in initializer lists used below
      26             :   template <class T1, class T2>
      27     8029911 :   constexpr ValueAndVariance(T1 t1, T2 t2) noexcept : value(t1), variance(t2) {}
      28        2118 :   constexpr ValueAndVariance(const double val) noexcept
      29        2118 :       : value(val), variance(0.0) {}
      30             :   T value;
      31             :   T variance;
      32             : 
      33             :   template <class T2>
      34           0 :   constexpr auto &operator=(const ValueAndVariance<T2> other) noexcept {
      35           0 :     value = other.value;
      36           0 :     variance = other.variance;
      37           0 :     return *this;
      38             :   }
      39             : 
      40           0 :   template <class T2> constexpr auto &operator=(const T2 other) noexcept {
      41           0 :     value = other;
      42           0 :     variance = 0.0;
      43           0 :     return *this;
      44             :   }
      45             : 
      46       63691 :   template <class T2> constexpr auto &operator+=(const T2 other) noexcept {
      47       63691 :     return *this = *this + other;
      48             :   }
      49           0 :   template <class T2> constexpr auto &operator-=(const T2 other) noexcept {
      50           0 :     return *this = *this - other;
      51             :   }
      52          84 :   template <class T2> constexpr auto &operator*=(const T2 other) noexcept {
      53          84 :     return *this = *this * other;
      54             :   }
      55           0 :   template <class T2> constexpr auto &operator/=(const T2 other) noexcept {
      56           0 :     return *this = *this / other;
      57             :   }
      58             : 
      59             :   template <class T2>
      60           0 :   explicit constexpr operator ValueAndVariance<T2>() const noexcept {
      61           0 :     return {static_cast<T2>(value), static_cast<T2>(variance)};
      62             :   }
      63             : };
      64             : 
      65             : template <class T>
      66          84 : constexpr auto operator-(const ValueAndVariance<T> a) noexcept {
      67          84 :   return ValueAndVariance{-a.value, a.variance};
      68             : }
      69             : 
      70             : template <class B, class E>
      71           9 : constexpr auto pow(const ValueAndVariance<B> base, const E exponent) noexcept {
      72           9 :   const auto pow_1 = numeric::pow(base.value, exponent - 1);
      73           9 :   const auto var_factor = std::abs(exponent) * pow_1;
      74           9 :   return ValueAndVariance{pow_1 * base.value,
      75           9 :                           var_factor * var_factor * base.variance};
      76             : }
      77             : 
      78           0 : template <class T> constexpr auto sqrt(const ValueAndVariance<T> a) noexcept {
      79             :   using std::sqrt;
      80           0 :   return ValueAndVariance{sqrt(a.value),
      81           0 :                           static_cast<T>(0.25 * (a.variance / a.value))};
      82             : }
      83             : 
      84        1695 : template <class T> constexpr auto abs(const ValueAndVariance<T> a) noexcept {
      85             :   using std::abs;
      86        1695 :   return ValueAndVariance{abs(a.value), a.variance};
      87             : }
      88             : 
      89           0 : template <typename T> constexpr auto exp(const ValueAndVariance<T> a) noexcept {
      90             :   using std::exp;
      91           0 :   const auto val = exp(a.value);
      92           0 :   return ValueAndVariance(val, val * val * a.variance);
      93             : }
      94             : 
      95           0 : template <typename T> constexpr auto log(const ValueAndVariance<T> a) noexcept {
      96             :   using std::log;
      97           0 :   return ValueAndVariance(log(a.value), a.variance / (a.value * a.value));
      98             : }
      99             : 
     100             : template <typename T>
     101           0 : constexpr auto log10(const ValueAndVariance<T> a) noexcept {
     102             :   using std::log10;
     103           0 :   const auto x = a.value * std::log(static_cast<T>(10.0L));
     104           0 :   return ValueAndVariance(log10(a.value), a.variance / (x * x));
     105             : }
     106             : 
     107        1612 : template <class T> constexpr auto isnan(const ValueAndVariance<T> a) noexcept {
     108             :   using numeric::isnan;
     109        1612 :   return isnan(a.value);
     110             : }
     111             : 
     112           2 : template <class T> constexpr auto isinf(const ValueAndVariance<T> a) noexcept {
     113             :   using numeric::isinf;
     114           2 :   return isinf(a.value);
     115             : }
     116             : 
     117             : template <class T>
     118          42 : constexpr auto isfinite(const ValueAndVariance<T> a) noexcept {
     119             :   using numeric::isfinite;
     120          42 :   return isfinite(a.value);
     121             : }
     122             : 
     123             : template <class T>
     124           0 : constexpr auto signbit(const ValueAndVariance<T> a) noexcept {
     125           0 :   return numeric::signbit(a.value);
     126             : }
     127             : 
     128             : template <class T>
     129           2 : constexpr auto isposinf(const ValueAndVariance<T> a) noexcept {
     130           2 :   return numeric::isinf(a.value) && !signbit(a);
     131             : }
     132             : 
     133             : template <class T>
     134           2 : constexpr auto isneginf(const ValueAndVariance<T> a) noexcept {
     135           2 :   return numeric::isinf(a.value) && signbit(a);
     136             : }
     137             : 
     138             : template <class T1, class T2>
     139       63706 : constexpr auto operator+(const ValueAndVariance<T1> a,
     140             :                          const ValueAndVariance<T2> b) noexcept {
     141       63706 :   return ValueAndVariance{a.value + b.value, a.variance + b.variance};
     142             : }
     143             : template <class T1, class T2>
     144           2 : constexpr auto operator-(const ValueAndVariance<T1> a,
     145             :                          const ValueAndVariance<T2> b) noexcept {
     146           2 :   return ValueAndVariance{a.value - b.value, a.variance + b.variance};
     147             : }
     148             : template <class T1, class T2>
     149           8 : constexpr auto operator*(const ValueAndVariance<T1> a,
     150             :                          const ValueAndVariance<T2> b) noexcept {
     151           8 :   return ValueAndVariance{a.value * b.value,
     152           8 :                           a.variance * b.value * b.value +
     153           8 :                               b.variance * a.value * a.value};
     154             : }
     155             : template <class T1, class T2>
     156          14 : constexpr auto operator/(const ValueAndVariance<T1> a,
     157             :                          const ValueAndVariance<T2> b) noexcept {
     158             :   return ValueAndVariance{
     159          14 :       a.value / b.value,
     160          14 :       (a.variance + b.variance * (a.value * a.value) / (b.value * b.value)) /
     161          14 :           (b.value * b.value)};
     162             : }
     163             : 
     164             : template <class T1, class T2>
     165           0 : constexpr auto operator+(const ValueAndVariance<T1> a, const T2 b) noexcept {
     166           0 :   return ValueAndVariance{a.value + b, a.variance};
     167             : }
     168             : template <class T1, class T2>
     169          17 : constexpr auto operator+(const T2 a, const ValueAndVariance<T1> b) noexcept {
     170          17 :   return ValueAndVariance{a + b.value, b.variance};
     171             : }
     172             : template <class T1, class T2>
     173        1655 : constexpr auto operator-(const ValueAndVariance<T1> a, const T2 b) noexcept {
     174        1655 :   return ValueAndVariance{a.value - b, a.variance};
     175             : }
     176             : template <class T1, class T2>
     177           0 : constexpr auto operator-(const T2 a, const ValueAndVariance<T1> b) noexcept {
     178           0 :   return ValueAndVariance{a - b.value, b.variance};
     179             : }
     180             : template <class T1, class T2>
     181         359 : constexpr auto operator*(const ValueAndVariance<T1> a, const T2 b) noexcept {
     182             :   // Order of operations to prevent integer overflow of `b*b`.
     183         359 :   return ValueAndVariance{a.value * b, a.variance * b * b};
     184             : }
     185             : template <class T1, class T2>
     186         132 : constexpr auto operator*(const T1 a, const ValueAndVariance<T2> b) noexcept {
     187             :   // Order of operations to prevent integer overflow of `a*a`.
     188         132 :   return ValueAndVariance{a * b.value, b.variance * a * a};
     189             : }
     190             : template <class T1, class T2>
     191           0 : constexpr auto operator/(const ValueAndVariance<T1> a, const T2 b) noexcept {
     192           0 :   const auto denominator = [b]() {
     193             :     if constexpr (std::is_integral_v<T2>)
     194             :       // Prevent int overflow.
     195           0 :       return static_cast<T1>(b) * static_cast<T1>(b);
     196             :     else
     197           0 :       return b * b;
     198           0 :   }();
     199           0 :   return ValueAndVariance{a.value / b, a.variance / denominator};
     200             : }
     201             : template <class T1, class T2>
     202           0 : constexpr auto operator/(const T1 a, const ValueAndVariance<T2> b) noexcept {
     203           0 :   return ValueAndVariance{a / b.value, b.variance * a * a /
     204           0 :                                            (b.value * b.value) /
     205           0 :                                            (b.value * b.value)};
     206             : }
     207             : 
     208             : // Comparison operators. Note that all of these IGNORE VARIANCES
     209             : template <class A, class B>
     210          11 : constexpr auto operator==(const ValueAndVariance<A> a,
     211             :                           const ValueAndVariance<B> b) noexcept {
     212          11 :   return a.value == b.value;
     213             : }
     214             : template <class A, class B>
     215           0 : constexpr auto operator==(const ValueAndVariance<A> a, const B b) noexcept {
     216           0 :   return a.value == b;
     217             : }
     218             : template <class A, class B>
     219           0 : constexpr auto operator==(const A a, const ValueAndVariance<B> b) noexcept {
     220           0 :   return a == b.value;
     221             : }
     222             : 
     223             : template <class A, class B>
     224           2 : constexpr auto operator!=(const ValueAndVariance<A> a,
     225             :                           const ValueAndVariance<B> b) noexcept {
     226           2 :   return !(a == b);
     227             : }
     228             : 
     229             : template <class A, class B>
     230           0 : constexpr auto operator!=(const ValueAndVariance<A> a, const B b) noexcept {
     231           0 :   return !(a == b);
     232             : }
     233             : 
     234             : template <class A, class B>
     235           0 : constexpr auto operator!=(const A a, const ValueAndVariance<B> b) noexcept {
     236           0 :   return !(a == b);
     237             : }
     238             : 
     239             : template <class A, class B>
     240          36 : constexpr auto operator<(const ValueAndVariance<A> a,
     241             :                          const ValueAndVariance<B> b) noexcept {
     242          36 :   return a.value < b.value;
     243             : }
     244             : template <class A, class B>
     245          53 : constexpr auto operator<(const ValueAndVariance<A> a, const B b) noexcept {
     246          53 :   return a.value < b;
     247             : }
     248             : template <class A, class B>
     249           0 : constexpr auto operator<(const A a, const ValueAndVariance<B> b) noexcept {
     250           0 :   return a < b.value;
     251             : }
     252             : 
     253             : template <class A, class B>
     254           2 : constexpr auto operator<=(const ValueAndVariance<A> a,
     255             :                           const ValueAndVariance<B> b) noexcept {
     256           2 :   return a.value <= b.value;
     257             : }
     258             : template <class A, class B>
     259        1656 : constexpr auto operator<=(const ValueAndVariance<A> a, const B b) noexcept {
     260        1656 :   return a.value <= b;
     261             : }
     262             : template <class A, class B>
     263           0 : constexpr auto operator<=(const A a, const ValueAndVariance<B> b) noexcept {
     264           0 :   return a <= b.value;
     265             : }
     266             : 
     267             : template <class A, class B>
     268           9 : constexpr auto operator>(const ValueAndVariance<A> a,
     269             :                          const ValueAndVariance<B> b) noexcept {
     270           9 :   return a.value > b.value;
     271             : }
     272             : template <class A, class B>
     273           9 : constexpr auto operator>(const ValueAndVariance<A> a, const B b) noexcept {
     274           9 :   return a.value > b;
     275             : }
     276             : template <class A, class B>
     277           0 : constexpr auto operator>(const A a, const ValueAndVariance<B> b) noexcept {
     278           0 :   return a > b.value;
     279             : }
     280             : 
     281             : template <class A, class B>
     282           2 : constexpr auto operator>=(const ValueAndVariance<A> a,
     283             :                           const ValueAndVariance<B> b) noexcept {
     284           2 :   return a.value >= b.value;
     285             : }
     286             : template <class A, class B>
     287           0 : constexpr auto operator>=(const ValueAndVariance<A> a, const B b) noexcept {
     288           0 :   return a.value >= b;
     289             : }
     290             : template <class A, class B>
     291           0 : constexpr auto operator>=(const A a, const ValueAndVariance<B> b) noexcept {
     292           0 :   return a >= b.value;
     293             : }
     294             : // end comparison operators
     295             : 
     296             : template <class T>
     297          48 : constexpr auto min(const ValueAndVariance<T> a,
     298             :                    const ValueAndVariance<T> b) noexcept {
     299          48 :   return a.value < b.value ? a : b;
     300             : }
     301             : template <class T>
     302         714 : constexpr auto max(const ValueAndVariance<T> a,
     303             :                    const ValueAndVariance<T> b) noexcept {
     304         714 :   return a.value > b.value ? a : b;
     305             : }
     306             : 
     307             : /// Deduction guide for class ValueAndVariances. Using decltype to deal with
     308             : /// potential mixed-type val and var arguments arising in binary operations
     309             : /// between, e.g., double and float.
     310             : template <class T1, class T2>
     311             : ValueAndVariance(const T1 &val, const T2 &var)
     312             :     -> ValueAndVariance<decltype(T1() + T2())>;
     313             : template <class T>
     314             : ValueAndVariance(const scipp::span<T> &val, const scipp::span<T> &var)
     315             :     -> ValueAndVariance<scipp::span<T>>;
     316             : 
     317             : template <class T> struct is_ValueAndVariance : std::false_type {};
     318             : template <class T>
     319             : struct is_ValueAndVariance<ValueAndVariance<T>> : std::true_type {};
     320             : template <class T>
     321             : inline constexpr bool is_ValueAndVariance_v = is_ValueAndVariance<T>::value;
     322             : 
     323             : } // namespace scipp::core
     324             : 
     325             : namespace scipp {
     326             : using core::is_ValueAndVariance_v;
     327             : using core::ValueAndVariance;
     328             : } // namespace scipp

Generated by: LCOV version 1.14