File indexing completed on 2025-04-19 09:13:40
0001
0002
0003
0004
0005
0006 #ifndef YODA_Estimate_h
0007 #define YODA_Estimate_h
0008
0009 #include "YODA/Exceptions.h"
0010 #include "YODA/Dbn.h"
0011 #include "YODA/Transformation.h"
0012 #include "YODA/Utils/MathUtils.h"
0013 #include "YODA/Utils/StringUtils.h"
0014 #include <cmath>
0015 #include <map>
0016 #include <string>
0017 #include <regex>
0018 #include <utility>
0019
0020 namespace YODA {
0021
0022
0023
0024
0025
0026
0027
0028
0029 class Estimate {
0030 public:
0031
0032
0033
0034
0035
0036 Estimate() { reset(); }
0037
0038
0039
0040
0041
0042 Estimate(double v, std::map<std::string,std::pair<double,double>>& errors)
0043 : _value(v), _error(errors) { }
0044
0045
0046
0047 Estimate(const double v, const std::pair<double,double>& e, const std::string& source = "")
0048 : _value(v) { setErr(e, source); }
0049
0050
0051
0052
0053 Estimate(const Estimate& toCopy) {
0054 _value = toCopy._value;
0055 _error = toCopy._error;
0056 }
0057
0058
0059
0060
0061 Estimate(Estimate&& toMove) {
0062 _value = std::move(toMove._value);
0063 _error = std::move(toMove._error);
0064 }
0065
0066
0067
0068
0069 Estimate& operator=(const Estimate& toCopy) noexcept {
0070 if (this != &toCopy) {
0071 _value = toCopy._value;
0072 _error = toCopy._error;
0073 }
0074 return *this;
0075 }
0076
0077
0078
0079
0080 Estimate& operator=(Estimate&& toMove) noexcept {
0081 if (this != &toMove) {
0082 _value = std::move(toMove._value);
0083 _error = std::move(toMove._error);
0084 }
0085 return *this;
0086 }
0087
0088
0089
0090
0091
0092
0093
0094
0095 Estimate& add(const Estimate& toAdd, const std::string& pat_uncorr="^stat|^uncor" ) {
0096 setVal(val() + toAdd.val());
0097 std::smatch match;
0098 const std::regex re(pat_uncorr, std::regex_constants::icase);
0099 for (const std::string& src : toAdd.sources()) {
0100 if (!hasSource(src)) setErr(toAdd.errDownUp(src), src);
0101 else if (std::regex_search(src, match, re)) {
0102
0103
0104 const auto [l_dn,l_up] = errDownUp(src);
0105 const auto [r_dn,r_up] = toAdd.errDownUp(src);
0106 const double new_dn = std::sqrt(l_dn*l_dn + r_dn*r_dn);
0107 const double new_up = std::sqrt(l_up*l_up + r_up*r_up);
0108 setErr({-new_dn,new_up}, src);
0109 }
0110 else {
0111
0112 const auto [l_dn,l_up] = errDownUp(src);
0113 const auto [r_dn,r_up] = toAdd.errDownUp(src);
0114 setErr({l_dn+r_dn, l_up+r_up}, src);
0115 }
0116 }
0117 return *this;
0118 }
0119
0120 Estimate& operator+=(const Estimate& toAdd) {
0121 return add(toAdd);
0122 }
0123
0124
0125 Estimate& subtract(const Estimate& toSubtract, const std::string& pat_uncorr="^stat|^uncor" ) {
0126 setVal(val() - toSubtract.val());
0127 std::smatch match;
0128 const std::regex re(pat_uncorr, std::regex_constants::icase);
0129 for (const std::string& src : toSubtract.sources()) {
0130 if (!hasSource(src)) setErr(toSubtract.errDownUp(src), src);
0131 else if (std::regex_search(src, match, re)) {
0132
0133 const auto [l_dn,l_up] = errDownUp(src);
0134 const auto [r_dn,r_up] = toSubtract.errDownUp(src);
0135 const double new_dn = std::sqrt(l_dn*l_dn + r_dn*r_dn);
0136 const double new_up = std::sqrt(l_up*l_up + r_up*r_up);
0137 setErr({-new_dn,new_up}, src);;
0138 }
0139 else {
0140
0141 const auto [l_dn,l_up] = errDownUp(src);
0142 const auto [r_dn,r_up] = toSubtract.errDownUp(src);
0143 setErr({l_dn+r_dn, l_up+r_up}, src);
0144 }
0145 }
0146 return *this;
0147 }
0148
0149 Estimate& operator-=(const Estimate& toSubtract) {
0150 return subtract(toSubtract);
0151 }
0152
0153
0154
0155
0156
0157
0158
0159 void setValue(const double value) noexcept { _value = value; }
0160
0161
0162 void setVal(const double val) noexcept { setValue(val); }
0163
0164
0165 void setErr(const std::pair<double, double> &err, const std::string& source = "") {
0166 const std::string& s = Utils::toUpper(source);
0167 if (s == "TOTAL")
0168 throw UserError("Use empty string for the total uncertainty!");
0169 _error[source] = err;
0170 }
0171
0172
0173
0174
0175
0176 void setErr(const double err, const std::string& source = "") {
0177 setErr({-fabs(err),fabs(err)}, source);
0178 }
0179
0180
0181 void set(const double val, const std::pair<double, double> &err, const std::string source = "") {
0182 setVal(val);
0183 setErr(err, source);
0184 }
0185
0186
0187
0188 void set(const double val, const double err, const std::string& source = "") {
0189 setVal(val);
0190 setErr(err, source);
0191 }
0192
0193
0194 void reset() noexcept {
0195 _value = std::numeric_limits<double>::quiet_NaN();
0196 _error.clear();
0197 }
0198
0199
0200
0201 void scale(const double scalefactor) noexcept {
0202 _value *= scalefactor;
0203 for (auto& item : _error) {
0204 item.second = { item.second.first * scalefactor, item.second.second * scalefactor };
0205 }
0206 }
0207
0208
0209 void scale(const Trf<1>& trf) {
0210 trf.transform(_value, _error);
0211 }
0212
0213
0214 void transform(const Trf<1>& trf) {
0215 scale(trf);
0216 }
0217
0218
0219 void renameSource(const std::string& old_label, const std::string& new_label) {
0220 if (!hasSource(old_label)) {
0221 throw UserError("Error map has no such key: "+old_label);
0222 }
0223 auto entry = _error.extract(old_label);
0224 entry.key() = new_label;
0225 _error.insert(std::move(entry));
0226 }
0227
0228
0229 void rmSource(const std::string& source) {
0230 if (hasSource(source)) {
0231 _error.erase(source);
0232 }
0233 }
0234
0235
0236 void rmErrs() {
0237 _error.clear();
0238 }
0239
0240
0241
0242 public:
0243
0244
0245
0246
0247
0248
0249 double val() const noexcept { return _value; }
0250
0251
0252
0253
0254
0255 std::pair<double,double> errDownUp(const std::string& source = "") const {
0256 const size_t count = _error.count(source);
0257 if (!count)
0258 throw RangeError("Error map has no such key: "+source);
0259 return _error.at(source);
0260 }
0261
0262
0263 std::pair<double,double> err(const std::string& source = "") const {
0264 return errDownUp(source);
0265 }
0266
0267
0268 std::pair<double,double> errNegPos(std::string source = "") const {
0269 return _downUp2NegPos( errDownUp(source) );
0270 }
0271
0272
0273 double errNeg(const std::string& source = "") const {
0274 return errNegPos(source).first;
0275 }
0276
0277
0278 double errPos(const std::string& source = "") const {
0279 return errNegPos(source).second;
0280 }
0281
0282
0283 double errDown(const std::string& source = "") const {
0284 return errDownUp(source).first;
0285 }
0286
0287
0288 double errUp(const std::string& source = "") const {
0289 return errDownUp(source).second;
0290 }
0291
0292
0293 double errAvg(const std::string& source = "") const {
0294 return _average(errDownUp(source));
0295 }
0296
0297
0298
0299 double errEnv(const std::string& source = "") const {
0300 return _envelope(errDownUp(source));
0301 }
0302
0303
0304 std::pair<double,double> relErrDownUp(const std::string& source = "") const {
0305 auto [dn,up] = errDownUp(source);
0306 dn = _value != 0 ? dn/fabs(_value) : std::numeric_limits<double>::quiet_NaN();
0307 up = _value != 0 ? up/fabs(_value) : std::numeric_limits<double>::quiet_NaN();
0308 return {dn,up};
0309 }
0310
0311
0312 std::pair<double,double> relErr(const std::string& source = "") const {
0313 return relErrDownUp(source);
0314 }
0315
0316
0317 double relErrDown(const std::string& source = "") const {
0318 return relErr(source).first;
0319 }
0320
0321
0322 double relErrUp(const std::string& source = "") const {
0323 return relErr(source).second;
0324 }
0325
0326
0327
0328 double relErrAvg(const std::string& source = "") const {
0329 return _average(relErrDownUp(source));
0330 }
0331
0332
0333
0334 double relErrEnv(const std::string& source = "") const {
0335 return _envelope(relErrDownUp(source));
0336 }
0337
0338
0339
0340 double valMax(const std::string& source = "") const {
0341 return val() + errPos(source);
0342 }
0343
0344
0345
0346 double valMin(const std::string& source = "") const {
0347 return val() + errNeg(source);
0348 }
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358 std::pair<double,double> quadSum(const std::string& pat_match = "") const noexcept {
0359 const bool check_match = pat_match != ""; std::smatch match;
0360 const std::regex re(pat_match, std::regex_constants::icase);
0361
0362 std::pair<double, double> ret = { 0., 0. };
0363 for (const auto& item : _error) {
0364 if (item.first == "") continue;
0365 if (check_match && !std::regex_search(item.first, match, re)) continue;
0366 const auto [dn,up] = _downUp2NegPos(item.second);
0367 ret.first += dn*dn;
0368 ret.second += up*up;
0369 }
0370 return { - std::sqrt(ret.first), std::sqrt(ret.second) };
0371 }
0372
0373
0374 double quadSumNeg(const std::string& pat_match = "") const noexcept {
0375 return quadSum(pat_match).first;
0376 }
0377
0378
0379 double quadSumPos(const std::string& pat_match = "") const noexcept {
0380 return quadSum(pat_match).second;
0381 }
0382
0383
0384 double quadSumAvg(const std::string& pat_match = "") const noexcept {
0385 return _average(quadSum(pat_match));
0386 }
0387
0388
0389
0390 double quadSumEnv(const std::string& pat_match = "") const noexcept {
0391 return _envelope(quadSum(pat_match));
0392 }
0393
0394
0395
0396 double quadSumMax(const std::string& pat_match = "") const {
0397 return val() + quadSumPos(pat_match);
0398 }
0399
0400
0401
0402 double quadSumMin(const std::string& pat_match = "") const {
0403 return val() + quadSumNeg(pat_match);
0404 }
0405
0406
0407
0408
0409
0410 std::pair<double,double> totalErr(const std::string& pat_match = "") const noexcept {
0411
0412 if (pat_match == "" && _error.count("")) return _downUp2NegPos(_error.at(""));
0413
0414 return quadSum(pat_match);
0415 }
0416
0417
0418 double totalErrNeg(const std::string& pat_match = "") const noexcept {
0419 return totalErr(pat_match).first;
0420 }
0421
0422
0423 double totalErrPos(const std::string& pat_match = "") const noexcept {
0424 return totalErr(pat_match).second;
0425 }
0426
0427
0428
0429 double totalErrAvg(const std::string& pat_match = "") const {
0430 return _average(totalErr(pat_match));
0431 }
0432
0433
0434
0435 double totalErrEnv(const std::string& pat_match = "") const {
0436 return _envelope(totalErr(pat_match));
0437 }
0438
0439
0440
0441 double totalErrMax(const std::string& pat_match = "") const {
0442 return val() + totalErrPos(pat_match);
0443 }
0444
0445
0446
0447 double totalErrMin(const std::string& pat_match = "") const {
0448 return val() + totalErrNeg(pat_match);
0449 }
0450
0451
0452 double valueErr(const std::string& pat_match = "") const {
0453 return fabs(totalErrAvg(pat_match));
0454 }
0455
0456
0457 std::pair<double,double> relTotalErr(const std::string& pat_match = "") const noexcept {
0458 auto [neg,pos] = totalErr(pat_match);
0459 neg = _value != 0 ? neg/fabs(_value) : std::numeric_limits<double>::quiet_NaN();
0460 pos = _value != 0 ? pos/fabs(_value) : std::numeric_limits<double>::quiet_NaN();
0461 return {neg,pos};
0462 }
0463
0464
0465 double relTotalErrNeg(const std::string& pat_match = "") const noexcept {
0466 return relTotalErr(pat_match).first;
0467 }
0468
0469
0470 double relTotalErrPos(const std::string& pat_match = "") const noexcept {
0471 return relTotalErr(pat_match).second;
0472 }
0473
0474
0475
0476 double relTotalErrAvg(const std::string& pat_match = "") const noexcept {
0477 return _average(relTotalErr(pat_match));
0478 }
0479
0480
0481
0482 double relTotalErrEnv(const std::string& pat_match = "") const noexcept {
0483 return _envelope(relTotalErr(pat_match));
0484 }
0485
0486
0487 std::vector<std::string> sources() const noexcept {
0488 std::vector<std::string> keys;
0489 for (const auto& item : _error) keys.push_back(item.first);
0490 return keys;
0491 }
0492
0493
0494 bool hasSource(const std::string& key) const noexcept {
0495 return _error.count(key);
0496 }
0497
0498
0499 size_t numErrs() const noexcept {
0500 return _error.size();
0501 }
0502
0503
0504
0505
0506
0507
0508 size_t _lengthContent(bool fixed_length = false) const noexcept {
0509 const size_t nErrs = fixed_length? 1 : numErrs();
0510 return 2*(nErrs + 1);
0511 }
0512
0513 std::vector<double> _serializeContent(bool fixed_length = false) const noexcept {
0514 std::vector<double> rtn;
0515 const size_t nErrs = fixed_length? 1 : numErrs();
0516 rtn.reserve(2*(nErrs + 1));
0517 rtn.push_back(_value);
0518 if (fixed_length) {
0519 rtn.push_back(1.0);
0520 rtn.push_back(totalErrNeg());
0521 rtn.push_back(totalErrPos());
0522 return rtn;
0523 }
0524 rtn.push_back((double)_error.size());
0525 for (const auto& item : _error) {
0526 rtn.push_back(item.second.first);
0527 rtn.push_back(item.second.second);
0528 }
0529 return rtn;
0530 }
0531
0532 void _deserializeContent(const std::vector<double>& data, bool fixed_length = false) {
0533
0534 if (data.size() < 2)
0535 throw UserError("Length of serialized data should be at least 2!");
0536
0537 if (2*(fixed_length? 1 : data[1]) != (data.size() - 2))
0538 throw UserError("Expected "+std::to_string(data[1])+" error pairs!");
0539
0540 reset();
0541 size_t idx = 0;
0542 auto itr = data.cbegin();
0543 const auto itrEnd = data.cend();
0544 while (itr != itrEnd) {
0545 if (!idx) {
0546 _value = *itr; ++itr; ++itr;
0547 }
0548 else {
0549 std::string name("source" + std::to_string(idx));
0550 const double dn = *itr; ++itr;
0551 const double up = *itr; ++itr;
0552 setErr({dn, up}, name);
0553 }
0554 ++idx;
0555 }
0556 if (numErrs() == 1) renameSource("source1", "");
0557 }
0558
0559 std::vector<std::string> serializeSources() const noexcept {
0560 return sources();
0561 }
0562
0563 void deserializeSources(const std::vector<std::string>& data) {
0564
0565 const size_t nErrs = numErrs();
0566 if (data.size() != nErrs)
0567 throw UserError("Expected " + std::to_string(nErrs) + " error source labels!");
0568
0569 for (size_t i = 0; i < data.size(); ++i) {
0570 const std::string name("source" + std::to_string(i+1));
0571 if (!_error.count(name))
0572 throw UserError("Key names have already been updated!");
0573 renameSource(name, data[i]);
0574 }
0575
0576 }
0577
0578
0579
0580 private:
0581
0582
0583
0584
0585
0586
0587
0588 std::pair<double,double> _downUp2NegPos(const std::pair<double,double> &e) const noexcept {
0589 const auto [dn,up] = e;
0590 if (dn < 0. && up < 0.) {
0591
0592 const double env = std::min(dn, up);
0593 return {env, 0.};
0594 }
0595 else if (dn < 0.) {
0596
0597 return {dn,up};
0598 }
0599 else if (up < 0.) {
0600
0601 return {up, dn};
0602 }
0603
0604 const double env = std::max(dn, up);
0605 return {0., env};
0606 }
0607
0608
0609 double _average(const std::pair<double,double> &err) const noexcept {
0610 return 0.5*(fabs(err.first) + fabs(err.second));
0611 }
0612
0613
0614 double _envelope(const std::pair<double,double> &err) const noexcept {
0615 return std::max(fabs(err.first), fabs(err.second));
0616 }
0617
0618
0619
0620
0621
0622
0623
0624
0625 double _value;
0626
0627
0628
0629
0630
0631
0632
0633 std::map<std::string, std::pair<double,double>> _error;
0634
0635
0636
0637 public:
0638
0639
0640
0641
0642 std::string _toString() const noexcept {
0643 std::string res ="";
0644 res += ("value="+ std::to_string(_value));
0645 for (const auto& item : _error) {
0646 const std::string name = item.first == ""? "user-supplied total " : "";
0647 res += (", " + name + "error("+ item.first);
0648 res += (")={ "+ std::to_string(item.second.first));
0649 res += (", "+ std::to_string(item.second.second)+" }");
0650 }
0651 return res;
0652 }
0653
0654
0655 };
0656
0657
0658
0659
0660
0661
0662 inline Estimate operator + (Estimate lhs, const Estimate& rhs) {
0663 lhs += rhs;
0664 return lhs;
0665 }
0666
0667
0668 inline Estimate operator + (Estimate lhs, Estimate&& rhs) {
0669 lhs += std::move(rhs);
0670 return lhs;
0671 }
0672
0673
0674 inline Estimate operator - (Estimate lhs, const Estimate& rhs) {
0675 lhs -= rhs;
0676 return lhs;
0677 }
0678
0679
0680 inline Estimate operator - (Estimate lhs, Estimate&& rhs) {
0681 lhs -= std::move(rhs);
0682 return lhs;
0683 }
0684
0685
0686 inline Estimate divide(const Estimate& numer, const Estimate& denom,
0687 const std::string& pat_uncorr="^stat|^uncor") {
0688
0689 Estimate rtn;
0690 if (denom.val()) rtn.setVal(numer.val() / denom.val());
0691 const double newVal = rtn.val();
0692
0693
0694 std::vector<std::string> sources = numer.sources();
0695 std::vector<std::string> tmp = denom.sources();
0696 sources.insert(std::end(sources),
0697 std::make_move_iterator(std::begin(tmp)),
0698 std::make_move_iterator(std::end(tmp)));
0699 std::sort(sources.begin(), sources.end());
0700 sources.erase( std::unique(sources.begin(), sources.end()), sources.end() );
0701
0702 std::smatch match;
0703 const std::regex re(pat_uncorr, std::regex_constants::icase);
0704 for (const std::string& src : sources) {
0705 if (std::regex_search(src, match, re)) {
0706
0707
0708 double n_dn = 0.0, n_up = 0.0;
0709 if (numer.hasSource(src)) {
0710 n_dn = numer.relErrDown(src);
0711 n_up = numer.relErrUp(src);
0712 }
0713 double d_dn = 0.0, d_up = 0.0;
0714 if (denom.hasSource(src)) {
0715 d_dn = denom.relErrDown(src);
0716 d_up = denom.relErrUp(src);
0717 }
0718 const double new_dn = fabs(newVal) * std::sqrt(n_dn*n_dn + d_dn*d_dn);
0719 const double new_up = fabs(newVal) * std::sqrt(n_up*n_up + d_up*d_up);
0720 rtn.setErr({-new_dn,new_up}, src);
0721 }
0722 else {
0723
0724
0725 double n_dn = numer.val(), n_up = numer.val();
0726 if (numer.hasSource(src)) {
0727 n_dn += numer.errDown(src);
0728 n_up += numer.errUp(src);
0729 }
0730 double d_dn = denom.val(), d_up = denom.val();
0731 if (denom.hasSource(src)) {
0732 d_dn += denom.errDown(src);
0733 d_up += denom.errUp(src);
0734 }
0735 double new_dn = std::numeric_limits<double>::quiet_NaN();
0736 double new_up = std::numeric_limits<double>::quiet_NaN();
0737 if (d_dn) new_dn = n_dn / d_dn - newVal;
0738 if (d_up) new_up = n_up / d_up - newVal;
0739 rtn.setErr({new_dn, new_up}, src);
0740 }
0741 }
0742
0743 return rtn;
0744 }
0745
0746
0747 inline Estimate operator / (const Estimate& numer, const Estimate& denom) {
0748 return divide(numer, denom);
0749 }
0750
0751
0752 inline Estimate operator / (Estimate&& numer, const Estimate& denom) {
0753 return divide(std::move(numer), denom);
0754 }
0755
0756
0757 inline Estimate operator / (const Estimate& numer, Estimate&& denom) {
0758 return divide(numer, std::move(denom));
0759 }
0760
0761
0762 inline Estimate operator / (Estimate&& numer, Estimate&& denom) {
0763 return divide(std::move(numer), std::move(denom));
0764 }
0765
0766
0767 inline Estimate efficiency(const Estimate& accepted, const Estimate& total,
0768 const std::string& pat_uncorr="^stat|^uncor") {
0769
0770
0771
0772
0773 const double acc_val = accepted.val();
0774 const double tot_val = total.val();
0775 if (acc_val > tot_val)
0776 throw UserError("Attempt to calculate an efficiency when the numerator is not a subset of the denominator: "
0777 + Utils::toStr(acc_val) + " / " + Utils::toStr(tot_val));
0778
0779 Estimate rtn = divide(accepted, total, pat_uncorr);
0780
0781
0782 std::vector<std::string> sources = accepted.sources();
0783 std::vector<std::string> tmp = total.sources();
0784 sources.insert(std::end(sources),
0785 std::make_move_iterator(std::begin(tmp)),
0786 std::make_move_iterator(std::end(tmp)));
0787 std::sort(sources.begin(), sources.end());
0788 sources.erase( std::unique(sources.begin(), sources.end()), sources.end() );
0789
0790
0791 std::smatch match;
0792 const double eff = rtn.val();
0793 const std::regex re(pat_uncorr, std::regex_constants::icase);
0794 for (const std::string& src : sources) {
0795 double err = std::numeric_limits<double>::quiet_NaN();
0796 if (!tot_val) {
0797 rtn.setErr({-err,err}, src);
0798 continue;
0799 }
0800 else if (std::regex_search(src, match, re)) {
0801 const double acc_err = accepted.relTotalErrAvg();
0802 const double tot_err = total.totalErrAvg();
0803 err = sqrt(std::abs( ((1-2*eff)*sqr(acc_err) + sqr(eff)*sqr(tot_err)) / sqr(tot_val) ));
0804 rtn.setErr({-err,err}, src);
0805 continue;
0806 }
0807 }
0808
0809 return rtn;
0810 }
0811
0812
0813
0814 }
0815
0816 #endif