Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
BinnedObservableId.h
Go to the documentation of this file.
1#ifndef BINNEDOBSERVABLEID_H
2#define BINNEDOBSERVABLEID_H
3
4#include <bit>
5#include <cmath>
6#include <cstdint>
7#include <cstdlib>
8#include <functional>
9#include <iomanip>
10#include <sstream>
11#include <stdexcept>
12#include <string>
13#include <tuple>
14#include <utility>
15#include <vector>
16
17#include "observable_ids.hpp"
18
41static inline double norm_zero(double x) noexcept {
42 return (x == 0.0) ? 0.0 : x;
43}
44
55static inline std::uint64_t bits_norm_zero(double x) noexcept {
56 x = norm_zero(x);
57 return std::bit_cast<std::uint64_t>(x);
58}
59
78struct EncodedBin {
79 long int_part;
80 long frac_part;
82};
83
90static constexpr std::size_t kBinnedFlhaInsertPos = 2;
91
98static constexpr std::size_t kBinnedFlhaPartCount = 6;
99
106static constexpr long kMaxBinFracDigits = 12;
107
117static inline long pow10_long(long n) noexcept {
118 long v = 1;
119 while (n-- > 0) {
120 v *= 10;
121 }
122 return v;
123}
124
142static inline std::string trim_decimal_string(std::string s) {
143 auto pos = s.find('.');
144 if (pos == std::string::npos) {
145 return s;
146 }
147
148 while (!s.empty() && s.back() == '0') {
149 s.pop_back();
150 }
151
152 if (!s.empty() && s.back() == '.') {
153 s.pop_back();
154 }
155
156 if (s.empty()) {
157 return "0";
158 }
159
160 return s;
161}
162
181static inline EncodedBin encode_bin_gev(double x) {
182 if (!std::isfinite(x)) {
183 throw std::runtime_error("Bin value must be finite");
184 }
185
186 x = norm_zero(x);
187
188 constexpr long double kScale = 1.0e12L;
189
190 long double xr = static_cast<long double>(x);
191 xr = std::round(xr * kScale) / kScale;
192
193 const bool neg = std::signbit(static_cast<double>(xr));
194 const long double ax = std::fabs(xr);
195
196 std::ostringstream oss;
197 oss << std::fixed << std::setprecision(kMaxBinFracDigits) << ax;
198
199 std::string s = trim_decimal_string(oss.str());
200
201 const auto pos = s.find('.');
202 std::string int_str = (pos == std::string::npos) ? s : s.substr(0, pos);
203 std::string frac_str = (pos == std::string::npos) ? "" : s.substr(pos + 1);
204
205 if (int_str.empty()) {
206 int_str = "0";
207 }
208
209 const long int_part = std::stol(int_str);
210 const long frac_part = frac_str.empty() ? 0L : std::stol(frac_str);
211 const long frac_digits = static_cast<long>(frac_str.size());
212
213 if (neg) {
214 if (int_part != 0) {
215 return EncodedBin{-int_part, frac_part, frac_digits};
216 }
217 if (frac_part != 0) {
218 return EncodedBin{0, -frac_part, frac_digits};
219 }
220 }
221
222 return EncodedBin{int_part, frac_part, frac_digits};
223}
224
241static inline double decode_bin_gev(long int_part, long frac_part, long frac_digits) {
242 if (frac_digits < 0 || frac_digits > kMaxBinFracDigits) {
243 throw std::runtime_error("decode_bin_gev: invalid frac_digits");
244 }
245
246 const long scale = pow10_long(frac_digits);
247 if (std::labs(frac_part) >= scale && frac_digits > 0) {
248 throw std::runtime_error("decode_bin_gev: frac_part does not fit frac_digits");
249 }
250
251 const bool neg = (int_part < 0) || (frac_part < 0);
252 const long abs_int = std::labs(int_part);
253 const long abs_frac = std::labs(frac_part);
254
255 long double out = static_cast<long double>(abs_int);
256
257 if (frac_digits > 0) {
258 out += static_cast<long double>(abs_frac) / static_cast<long double>(scale);
259 }
260
261 if (neg) {
262 out = -out;
263 }
264
265 return norm_zero(static_cast<double>(out));
266}
267
287 std::pair<double, double> p;
295
304 BinnedObservableId(ObservableId id) : s(id), p({0.0, 0.0}) {}
305
312 BinnedObservableId(ObservableId id, std::pair<double, double> bin) : s(id), p(bin) {}
313
322 BinnedObservableId(Observables id) : s(ObservableMapper::to_id(id)), p({0.0, 0.0}) {}
323
330 BinnedObservableId(Observables id, std::pair<double, double> bin) : s(ObservableMapper::to_id(id)), p(bin) {}
331
343 bool operator==(BinnedObservableId const& o) const noexcept {
344 return s == o.s
345 && norm_zero(p.first) == norm_zero(o.p.first)
346 && norm_zero(p.second) == norm_zero(o.p.second);
347 }
348
359 bool operator<(BinnedObservableId const& o) const noexcept {
360 const std::uint64_t a = bits_norm_zero(p.first);
361 const std::uint64_t b = bits_norm_zero(p.second);
362 const std::uint64_t c = bits_norm_zero(o.p.first);
363 const std::uint64_t d = bits_norm_zero(o.p.second);
364
365 return std::tie(s, a, b) < std::tie(o.s, c, d);
366 }
367
384 LhaID flha() const {
385 auto flha_opt = ObservableMapper::flha_of(this->s);
386 if (!flha_opt.has_value()) {
387 throw std::runtime_error("ObservableId to flha mapping unknown");
388 }
389
390 LhaID unbinned_id = flha_opt.value();
391
392 const EncodedBin low = encode_bin_gev(this->p.first);
393 const EncodedBin high = encode_bin_gev(this->p.second);
394
395 std::vector<long> bin_parts{
396 low.int_part,
397 low.frac_part,
398 low.frac_digits,
399 high.int_part,
400 high.frac_part,
401 high.frac_digits
402 };
403
404 auto parts = unbinned_id.get_parts();
405 parts.insert(parts.begin() + kBinnedFlhaInsertPos, bin_parts.begin(), bin_parts.end());
406
407 return LhaID(parts);
408 }
409
428 auto parts = id.get_parts();
429
430 if (parts.size() < kBinnedFlhaInsertPos + kBinnedFlhaPartCount) {
431 throw std::runtime_error("from_flha: LhaID has not enough parts to contain robust binning");
432 }
433
434 const long low_int = parts.at(kBinnedFlhaInsertPos + 0);
435 const long low_frac = parts.at(kBinnedFlhaInsertPos + 1);
436 const long low_ndigits = parts.at(kBinnedFlhaInsertPos + 2);
437 const long high_int = parts.at(kBinnedFlhaInsertPos + 3);
438 const long high_frac = parts.at(kBinnedFlhaInsertPos + 4);
439 const long high_ndigits = parts.at(kBinnedFlhaInsertPos + 5);
440
441 parts.erase(parts.begin() + kBinnedFlhaInsertPos,
442 parts.begin() + kBinnedFlhaInsertPos + kBinnedFlhaPartCount);
443 LhaID unbinned_id(parts);
444
445 auto obs_opt = ObservableMapper::from_flha(unbinned_id);
446 if (!obs_opt.has_value()) {
447 throw std::runtime_error("from_flha: flha to ObservableId mapping unknown");
448 }
449
451 out.s = obs_opt.value();
452 out.p = {
453 decode_bin_gev(low_int, low_frac, low_ndigits),
454 decode_bin_gev(high_int, high_frac, high_ndigits)
455 };
456
457 return out;
458 }
459
468 std::string str() const {
469 std::stringstream ss;
470 ss << s.str() << " [" << p.first << ", " << p.second << "]";
471 return ss.str();
472 }
473};
474
483template<>
484struct std::hash<BinnedObservableId> {
494 std::size_t operator()(BinnedObservableId const& id) const noexcept {
495 std::size_t h = std::hash<ObservableId>{}(id.s);
496
497 auto mix = [](std::size_t& seed, std::size_t v) {
498 seed ^= v + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
499 };
500
501 mix(h, std::hash<std::uint64_t>{}(bits_norm_zero(id.p.first)));
502 mix(h, std::hash<std::uint64_t>{}(bits_norm_zero(id.p.second)));
503
504 return h;
505 }
506};
507
518inline std::ostream& operator<<(std::ostream& os, BinnedObservableId const& id) {
519 return os << id.str();
520}
521
522#endif // BINNEDOBSERVABLEID_H
std::ostream & operator<<(std::ostream &os, BinnedObservableId const &id)
Streams a human-readable binned observable identifier.
Observables
Definition GeneralEnum.h:4
High-level mapper for observable names, FLHA ids and parent decays.
static std::optional< ObservableId > from_flha(const LhaID &ext)
Resolve a FLHA id to a dynamic observable id.
static std::optional< LhaID > flha_of(const ObservableId &id)
Return the FLHA id attached to an observable id, if any.
const std::string & str() const
Returns the underlying string.
csl::Expr v
Definition sm.h:110
Mapper for builtin and runtime observable identifiers.
Identifies an observable together with a numerical bin.
BinnedObservableId(ObservableId id, std::pair< double, double > bin)
Constructs a binned observable identifier.
bool operator==(BinnedObservableId const &o) const noexcept
Equality comparison operator.
bool operator<(BinnedObservableId const &o) const noexcept
Strict weak ordering for binned observable identifiers.
LhaID flha() const
Converts this binned observable identifier to its FLHA encoding.
BinnedObservableId(ObservableId id)
Constructs an unbinned observable identifier.
BinnedObservableId(Observables id, std::pair< double, double > bin)
Constructs a binned observable identifier.
static BinnedObservableId from_flha(LhaID const &id)
Reconstructs a binned observable identifier from an FLHA encoding.
BinnedObservableId()=default
Default constructor.
BinnedObservableId(Observables id)
Constructs an unbinned observable identifier.
std::pair< double, double > p
std::string str() const
Returns a human-readable representation of the binned observable.
Integer representation of a decimal bin boundary.
Represents an identifier of a LHA element, possibly containing several sub-ids.
Definition LhaID.h:56
std::vector< long > get_parts() const
Returns the underlying vector of sub-ids.
Definition LhaID.h:148
std::size_t operator()(BinnedObservableId const &id) const noexcept
Computes the hash of a binned observable identifier.