Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooNaNPacker.h
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Author:
4 * Stephan Hageboeck, CERN 2020
5 *
6 * Copyright (c) 2023, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
13#ifndef ROOFIT_BATCHCOMPUTE_ROONANPACKER_H
14#define ROOFIT_BATCHCOMPUTE_ROONANPACKER_H
15
17
18#include <TError.h>
19
20#include <limits>
21#include <cstdint>
22#include <cmath>
23#include <cassert>
24#include <numeric>
25#include <cstring>
26
27/// Little struct that can pack a float into the unused bits of the mantissa of a
28/// NaN double. This can be used to transport information about violation
29/// of function definition ranges, negative PDFs or other computation
30/// problems in RooFit.
31/// To separate NaNs that contain packed floats from regular NaNs, a tag is
32/// written into the upper bits of the mantissa. If this tag is found, a payload
33/// can be recovered. Otherwise, the NaN is assumed to originate from other sources
34/// than a RooFit class that wants to signal to the minimiser.
36 double _payload;
37
38 // In double-valued NaNs, on can abuse the lowest 51 bit for payloads.
39 // We use this to pack a float into the lowest 32 bits, which leaves
40 // 19-bit to include a magic tag to tell NaNs with payload from ordinary
41 // NaNs:
42 static constexpr uint64_t magicTagMask = 0x3ffff00000000;
43 static constexpr uint64_t magicTag = 0x321ab00000000;
44
45 constexpr RooNaNPacker() : _payload(0.) {}
46
47 /// Create NaN with a packed floating point number.
49
50 /// Pack float into mantissa of NaN.
51 void setPayload(float payload)
52 {
53 _payload = packFloatIntoNaN(payload);
54
55 if (!std::isnan(_payload)) {
56 // Big-endian machine or other. Just return NaN.
57 warn();
58 _payload = std::numeric_limits<double>::quiet_NaN();
59 }
60 }
61
62 /// Accumulate a packed float from another NaN into `this`.
63 void accumulate(double val) { *this += unpackNaN(val); }
64
65 /// Unpack floats from NaNs, and sum the packed values.
66 template <class It_t>
67 static double accumulatePayloads(It_t begin, It_t end)
68 {
69 double sum = std::accumulate(begin, end, 0.f, [](float acc, double val) { return acc += unpackNaN(val); });
70
71 return packFloatIntoNaN(sum);
72 }
73
74 /// Add to the packed float.
76 {
77 setPayload(getPayload() + val);
78 return *this;
79 }
80
81 /// Multiply the packed float.
83 {
84 setPayload(getPayload() * val);
85 return *this;
86 }
87
88 /// Retrieve packed float. Returns zero if number is not NaN
89 /// or if float wasn't packed by this class.
90 float getPayload() const { return isNaNWithPayload(_payload) ? unpackNaN(_payload) : 0.; }
91
92 /// Retrieve a NaN with the current float payload packed into the mantissa.
93 double getNaNWithPayload() const { return _payload; }
94
95 /// Test if this struct has a float packed into its mantissa.
96 bool isNaNWithPayload() const { return isNaNWithPayload(_payload); }
97
98 /// Test if `val` has a float packed into its mantissa.
99 static bool isNaNWithPayload(double val)
100 {
101 uint64_t tmp;
102 std::memcpy(&tmp, &val, sizeof(uint64_t));
103 return std::isnan(val) && (tmp & magicTagMask) == magicTag;
104 }
105
106 /// Pack float into mantissa of a NaN. Adds a tag to the
107 /// upper bits of the mantissa, so a "normal" NaN can be
108 /// differentiated from a NaN with a payload.
109 __roodevice__ __roohost__ static double packFloatIntoNaN(float payload)
110 {
111 double result = std::numeric_limits<double>::quiet_NaN();
112 uint64_t tmp;
113 std::memcpy(&tmp, &result, sizeof(uint64_t));
114 tmp |= magicTag;
115 std::memcpy(&tmp, &payload, sizeof(float));
116 std::memcpy(&result, &tmp, sizeof(uint64_t));
117 return result;
118 }
119
120 /// If `val` is NaN and a this NaN has been tagged as containing
121 /// a payload, unpack the float from the mantissa.
122 /// Return 0 otherwise.
123 static float unpackNaN(double val)
124 {
125 float tmp;
126 std::memcpy(&tmp, &val, sizeof(float));
127 return isNaNWithPayload(val) ? tmp : 0.;
128 }
129
130 /// Warn that packing only works on little-endian machines.
131 static void warn()
132 {
133 static bool haveWarned = false;
134 if (!haveWarned)
135 Warning("RooNaNPacker",
136 "Fast recovery from undefined function values only implemented for little-endian machines."
137 " If necessary, request an extension of functionality on https://root.cern");
138 haveWarned = true;
139 }
140};
141
142#endif /* ROOFIT_ROOFITCORE_INC_ROONANPACKER_H_ */
#define __roohost__
#define __roodevice__
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:241
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 result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Little struct that can pack a float into the unused bits of the mantissa of a NaN double.
static constexpr uint64_t magicTag
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
__roodevice__ static __roohost__ double packFloatIntoNaN(float payload)
Pack float into mantissa of a NaN.
float getPayload() const
Retrieve packed float.
static bool isNaNWithPayload(double val)
Test if val has a float packed into its mantissa.
static constexpr uint64_t magicTagMask
double _payload
void setPayload(float payload)
Pack float into mantissa of NaN.
constexpr RooNaNPacker()
RooNaNPacker(float value)
Create NaN with a packed floating point number.
double getNaNWithPayload() const
Retrieve a NaN with the current float payload packed into the mantissa.
RooNaNPacker & operator*=(float val)
Multiply the packed float.
static double accumulatePayloads(It_t begin, It_t end)
Unpack floats from NaNs, and sum the packed values.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
void accumulate(double val)
Accumulate a packed float from another NaN into this.
RooNaNPacker & operator+=(float val)
Add to the packed float.
static void warn()
Warn that packing only works on little-endian machines.
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2345