Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Util.h
Go to the documentation of this file.
1// @(#)root/mathcore:$Id$
2// Author: L. Moneta Tue Nov 14 15:44:38 2006
3
4/**********************************************************************
5 * *
6 * Copyright (c) 2006 LCG ROOT Math Team, CERN/PH-SFT *
7 * *
8 * *
9 **********************************************************************/
10
11// Utility functions for all ROOT Math classes
12
13#ifndef ROOT_Math_Util
14#define ROOT_Math_Util
15
16#include <algorithm>
17#include <chrono>
18#include <cmath>
19#include <functional>
20#include <limits>
21#include <numeric>
22#include <sstream>
23#include <string>
24
25// This can be protected against by defining ROOT_Math_VecTypes
26// This is only used for the R__HAS_STD_EXPERIMENTAL_SIMD define
27// and a single VecCore function in EvalLog
28#ifndef ROOT_Math_VecTypes
29#include "Types.h"
30#endif
31
32
33// for defining unused variables in the interfaces
34// and have still them in the documentation
35#define MATH_UNUSED(var) (void)var
36
37
38namespace ROOT {
39
40namespace Math {
41
42/**
43 namespace defining Utility functions needed by mathcore
44*/
45namespace Util {
46
48
49public:
50 TimingScope(std::function<void(std::string const&)> printer, std::string const &message);
51
53
54private:
55 std::chrono::steady_clock::time_point fBegin;
56 std::function<void(std::string const&)> fPrinter;
57 const std::string fMessage;
58};
59
60 /**
61 Utility function for conversion to strings
62 */
63 template <class T>
64 std::string ToString(const T &val)
65 {
66 std::ostringstream buf;
67 buf << val;
68
69 std::string ret = buf.str();
70 return ret;
71 }
72
73 /// safe evaluation of log(x) with a protections against negative or zero argument to the log
74 /// smooth linear extrapolation below function values smaller than epsilon
75 /// (better than a simple cut-off)
76
77 template<class T>
78 inline T EvalLog(T x) {
79 static const T epsilon = T(2.0 * std::numeric_limits<double>::min());
80
81 if constexpr (std::is_arithmetic_v<T>) {
82 return x <= epsilon ? x / epsilon + std::log(epsilon) - T(1.0) : std::log(x);
83 } else {
84 // assume std::simd for non-arithmetic types
85 T logval{};
86 auto mask = x <= epsilon;
87 where(mask, logval) = x / epsilon + log(epsilon) - T(1.0);
88 where(!mask, logval) = log(x);
89 return logval;
90 }
91 }
92
93 } // end namespace Util
94
95 /// \class KahanSum
96 /// The Kahan summation is a compensated summation algorithm, which significantly reduces numerical errors
97 /// when adding a sequence of finite-precision floating point numbers.
98 /// This is done by keeping a separate running compensation (a variable to accumulate small errors).
99 ///
100 /// ### Auto-vectorisable accumulation
101 /// This class can internally use multiple accumulators (template parameter `N`).
102 /// When filled from a collection that supports index access from a *contiguous* block of memory,
103 /// compilers such as gcc, clang and icc can auto-vectorise the accumulation. This happens by cycling
104 /// through the internal accumulators based on the value of "`index % N`", so `N` accumulators can be filled from a block
105 /// of `N` numbers in a single instruction.
106 ///
107 /// The usage of multiple accumulators might slightly increase the precision in comparison to the single-accumulator version
108 /// with `N = 1`.
109 /// This depends on the order and magnitude of the numbers being accumulated. Therefore, in rare cases, the accumulation
110 /// result can change *in dependence of N*, even when the data are identical.
111 /// The magnitude of such differences is well below the precision of the floating point type, and will therefore mostly show
112 /// in the compensation sum(see Carry()). Increasing the number of accumulators therefore only makes sense to
113 /// speed up the accumulation, but not to increase precision.
114 ///
115 /// \param T The type of the values to be accumulated.
116 /// \param N Number of accumulators. Defaults to 1. Ideal values are the widths of a vector register on the relevant architecture.
117 /// Depending on the instruction set, good values are:
118 /// - AVX2-float: 8
119 /// - AVX2-double: 4
120 /// - AVX512-float: 16
121 /// - AVX512-double: 8
122 ///
123 /// ### Examples
124 ///
125 /// ~~~{.cpp}
126 /// std::vector<double> numbers(1000);
127 /// for (std::size_t i=0; i<1000; ++i) {
128 /// numbers[i] = rand();
129 /// }
130 ///
131 /// ROOT::Math::KahanSum<double, 4> k;
132 /// k.Add(numbers.begin(), numbers.end());
133 /// // or
134 /// k.Add(numbers);
135 /// ~~~
136 /// ~~~{.cpp}
137 /// double offset = 10.;
138 /// auto result = ROOT::Math::KahanSum<double, 4>::Accumulate(numbers.begin(), numbers.end(), offset);
139 /// ~~~
140 template<typename T = double, unsigned int N = 1>
141 class KahanSum {
142 public:
143 /// Initialise the sum.
144 /// \param[in] initialValue Initialise with this value. Defaults to 0.
145 explicit KahanSum(T initialValue = T{}) {
146 fSum[0] = initialValue;
147 std::fill(std::begin(fSum)+1, std::end(fSum), 0.);
148 std::fill(std::begin(fCarry), std::end(fCarry), 0.);
149 }
150
151 /// Initialise with a sum value and a carry value.
152 /// \param[in] initialSumValue Initialise the sum with this value.
153 /// \param[in] initialCarryValue Initialise the carry with this value.
157 std::fill(std::begin(fSum)+1, std::end(fSum), 0.);
158 std::fill(std::begin(fCarry)+1, std::end(fCarry), 0.);
159 }
160
161 /// Initialise the sum with a pre-existing state.
162 /// \param[in] sumBegin Begin of iterator range with values to initialise the sum with.
163 /// \param[in] sumEnd End of iterator range with values to initialise the sum with.
164 /// \param[in] carryBegin Begin of iterator range with values to initialise the carry with.
165 /// \param[in] carryEnd End of iterator range with values to initialise the carry with.
166 template<class Iterator>
167 KahanSum(Iterator sumBegin, Iterator sumEnd, Iterator carryBegin, Iterator carryEnd) {
168 assert(std::distance(sumBegin, sumEnd) == N);
169 assert(std::distance(carryBegin, carryEnd) == N);
170 std::copy(sumBegin, sumEnd, std::begin(fSum));
171 std::copy(carryBegin, carryEnd, std::begin(fCarry));
172 }
173
174 /// Constructor to create a KahanSum from another KahanSum with a different number of accumulators
175 template <unsigned int M>
177 fSum[0] = other.Sum();
178 fCarry[0] = other.Carry();
179 std::fill(std::begin(fSum)+1, std::end(fSum), 0.);
180 std::fill(std::begin(fCarry)+1, std::end(fCarry), 0.);
181 }
182
183 /// Single-element accumulation. Will not vectorise.
184 void Add(T x) {
185 auto y = x - fCarry[0];
186 auto t = fSum[0] + y;
187 fCarry[0] = (t - fSum[0]) - y;
188 fSum[0] = t;
189 }
190
191
192 /// Accumulate from a range denoted by iterators.
193 ///
194 /// This function will auto-vectorise with random-access iterators.
195 /// \param[in] begin Beginning of a range. Needs to be a random access iterator for automatic
196 /// vectorisation, because a contiguous block of memory needs to be read.
197 /// \param[in] end End of the range.
198 template <class Iterator>
199 void Add(Iterator begin, Iterator end) {
200 static_assert(std::is_floating_point<
201 typename std::remove_reference<decltype(*begin)>::type>::value,
202 "Iterator needs to point to floating-point values.");
203 const std::size_t n = std::distance(begin, end);
204
205 for (std::size_t i=0; i<n; ++i) {
206 AddIndexed(*(begin++), i);
207 }
208 }
209
210
211 /// Fill from a container that supports index access.
212 /// \param[in] inputs Container with index access such as std::vector or array.
213 template<class Container_t>
214 void Add(const Container_t& inputs) {
215 static_assert(std::is_floating_point<typename Container_t::value_type>::value,
216 "Container does not hold floating-point values.");
217 for (std::size_t i=0; i < inputs.size(); ++i) {
218 AddIndexed(inputs[i], i);
219 }
220 }
221
222
223 /// Iterate over a range and return an instance of a KahanSum.
224 ///
225 /// See Add(Iterator,Iterator) for details.
226 /// \param[in] begin Beginning of a range.
227 /// \param[in] end End of the range.
228 /// \param[in] initialValue Optional initial value.
229 template <class Iterator>
230 static KahanSum<T, N> Accumulate(Iterator begin, Iterator end,
231 T initialValue = T{}) {
233 theSum.Add(begin, end);
234
235 return theSum;
236 }
237
238
239 /// Add `input` to the sum.
240 ///
241 /// Particularly helpful when filling from a for loop.
242 /// This function can be inlined and auto-vectorised if
243 /// the index parameter is used to enumerate *consecutive* fills.
244 /// Use Add() or Accumulate() when no index is available.
245 /// \param[in] input Value to accumulate.
246 /// \param[in] index Index of the value. Determines internal accumulator that this
247 /// value is added to. Make sure that consecutive fills have consecutive indices
248 /// to make a loop auto-vectorisable. The actual value of the index does not matter,
249 /// as long as it is consecutive.
250 void AddIndexed(T input, std::size_t index) {
251 const unsigned int i = index % N;
252 const T y = input - fCarry[i];
253 const T t = fSum[i] + y;
254 fCarry[i] = (t - fSum[i]) - y;
255 fSum[i] = t;
256 }
257
258 /// \return Compensated sum.
259 T Sum() const {
260 return std::accumulate(std::begin(fSum), std::end(fSum), 0.);
261 }
262
263 /// \return Compensated sum.
264 T Result() const {
265 return Sum();
266 }
267
268 /// \return The sum used for compensation.
269 T Carry() const {
270 return std::accumulate(std::begin(fCarry), std::end(fCarry), 0.);
271 }
272
273 /// Add `arg` into accumulator. Does not vectorise.
275 Add(arg);
276 return *this;
277 }
278
279 /// Add other KahanSum into accumulator. Does not vectorise.
280 ///
281 /// Based on KahanIncrement from:
282 /// Y. Tian, S. Tatikonda and B. Reinwald, "Scalable and Numerically Stable Descriptive Statistics in SystemML," 2012 IEEE 28th International Conference on Data Engineering, 2012, pp. 1351-1359, doi: 10.1109/ICDE.2012.12.
283 /// Note that while Tian et al. add the carry in the first step, we subtract
284 /// the carry, in accordance with the Add(Indexed) implementation(s) above.
285 /// This is purely an implementation choice that has no impact on performance.
286 ///
287 /// \note Take care when using += (and -=) to add other KahanSums into a zero-initialized
288 /// KahanSum. The operator behaves correctly in this case, but the result may be slightly
289 /// off if you expect 0 + x to yield exactly x (where 0 is the zero-initialized KahanSum
290 /// and x another KahanSum). In particular, x's carry term may get lost. This doesn't
291 /// just happen with zero-initialized KahanSums; see the SubtractWithABitTooSmallCarry
292 /// test case in the testKahan unittest for other examples. This behavior is internally
293 /// consistent: the carry also gets lost if you switch the operands and it also happens with
294 /// other KahanSum operators.
295 template<typename U, unsigned int M>
297 U corrected_arg_sum = other.Sum() - (fCarry[0] + other.Carry());
298 U sum = fSum[0] + corrected_arg_sum;
300 fSum[0] = sum;
301 fCarry[0] = correction;
302 return *this;
303 }
304
305 /// Subtract other KahanSum. Does not vectorise.
306 ///
307 /// Based on KahanIncrement from: Tian et al., 2012 (see operator+= documentation).
308 template<typename U, unsigned int M>
310 U corrected_arg_sum = -other.Sum() - (fCarry[0] - other.Carry());
311 U sum = fSum[0] + corrected_arg_sum;
313 fSum[0] = sum;
314 fCarry[0] = correction;
315 return *this;
316 }
317
319 {
320 return {-this->fSum[0], -this->fCarry[0]};
321 }
322
323 template<typename U, unsigned int M>
324 bool operator ==(KahanSum<U, M> const& other) const {
325 return (this->Sum() == other.Sum()) && (this->Carry() == other.Carry());
326 }
327
328 template<typename U, unsigned int M>
329 bool operator !=(KahanSum<U, M> const& other) const {
330 return !(*this == other);
331 }
332
333 private:
334 T fSum[N];
336 };
337
338 /// Add two non-vectorized KahanSums.
339 template<typename T, unsigned int N, typename U, unsigned int M>
341 KahanSum<T, N> sum(left);
342 sum += right;
343 return sum;
344 }
345
346 /// Subtract two non-vectorized KahanSums.
347 template<typename T, unsigned int N, typename U, unsigned int M>
349 KahanSum<T, N> sum(left);
350 sum -= right;
351 return sum;
352 }
353
354 } // end namespace Math
355
356} // end namespace ROOT
357
358
359#endif /* ROOT_Math_Util */
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t mask
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
The Kahan summation is a compensated summation algorithm, which significantly reduces numerical error...
Definition Util.h:141
T Sum() const
Definition Util.h:259
bool operator==(KahanSum< U, M > const &other) const
Definition Util.h:324
KahanSum< T, N > operator-()
Definition Util.h:318
static KahanSum< T, N > Accumulate(Iterator begin, Iterator end, T initialValue=T{})
Iterate over a range and return an instance of a KahanSum.
Definition Util.h:230
T Result() const
Definition Util.h:264
void Add(Iterator begin, Iterator end)
Accumulate from a range denoted by iterators.
Definition Util.h:199
void Add(const Container_t &inputs)
Fill from a container that supports index access.
Definition Util.h:214
KahanSum(KahanSum< T, M > const &other)
Constructor to create a KahanSum from another KahanSum with a different number of accumulators.
Definition Util.h:176
bool operator!=(KahanSum< U, M > const &other) const
Definition Util.h:329
T Carry() const
Definition Util.h:269
void AddIndexed(T input, std::size_t index)
Add input to the sum.
Definition Util.h:250
KahanSum< T, N > & operator+=(T arg)
Add arg into accumulator. Does not vectorise.
Definition Util.h:274
KahanSum< T, N > & operator+=(const KahanSum< U, M > &other)
Add other KahanSum into accumulator.
Definition Util.h:296
KahanSum(T initialValue=T{})
Initialise the sum.
Definition Util.h:145
KahanSum(T initialSumValue, T initialCarryValue)
Initialise with a sum value and a carry value.
Definition Util.h:154
void Add(T x)
Single-element accumulation. Will not vectorise.
Definition Util.h:184
KahanSum< T, N > & operator-=(KahanSum< U, M > const &other)
Subtract other KahanSum.
Definition Util.h:309
KahanSum(Iterator sumBegin, Iterator sumEnd, Iterator carryBegin, Iterator carryEnd)
Initialise the sum with a pre-existing state.
Definition Util.h:167
std::chrono::steady_clock::time_point fBegin
Definition Util.h:55
const std::string fMessage
Definition Util.h:57
std::function< void(std::string const &) fPrinter)
Definition Util.h:56
TimingScope(std::function< void(std::string const &)> printer, std::string const &message)
Definition Util.cxx:3
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
Namespace for new Math classes and functions.
T EvalLog(T x)
safe evaluation of log(x) with a protections against negative or zero argument to the log smooth line...
Definition Util.h:78
std::string ToString(const T &val)
Utility function for conversion to strings.
Definition Util.h:64
DisplacementVector2D< CoordSystem1, U > operator+(DisplacementVector2D< CoordSystem1, U > v1, const DisplacementVector2D< CoordSystem2, U > &v2)
Addition of DisplacementVector2D vectors.
DisplacementVector2D< CoordSystem1, U > operator-(DisplacementVector2D< CoordSystem1, U > v1, DisplacementVector2D< CoordSystem2, U > const &v2)
Difference between two DisplacementVector2D vectors.
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2339