cepgen is hosted by Hepforge, IPPP Durham
CepGen 1.2.5
Central exclusive processes event generator
Loading...
Searching...
No Matches
Hist1D.cpp
Go to the documentation of this file.
1/*
2 * CepGen: a central exclusive processes event generator
3 * Copyright (C) 2021-2024 Laurent Forthomme
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <gsl/gsl_errno.h>
20
21#include <cmath>
22
27#include "CepGen/Utils/String.h"
28
29namespace cepgen {
30 namespace utils {
32 : Drawable(params.get<std::string>("name"), params.get<std::string>("title")) {
33 if (const auto& xbins = params.get<std::vector<double> >("xbins"); xbins.size() > 1)
34 buildFromBins(xbins);
35 else if (const auto& xrange = params.get<Limits>("xrange"); xrange.valid())
36 buildFromRange(params.get<int>("nbins") > 0 ? params.get<int>("nbins") : params.get<int>("nbinsX"), xrange);
37 else
38 throw CG_FATAL("Hist1D") << "Failed to build a 1D histogram with user parameters: " << params << ".";
39 }
40
41 Hist1D::Hist1D(size_t num_bins_x, const Limits& xrange, const std::string& name, const std::string& title)
42 : Drawable(name, title) {
43 if (num_bins_x == 0)
44 throw CG_ERROR("Hist1D") << "Number of bins must be strictly positive!";
45 buildFromRange(num_bins_x, xrange);
46 }
47
48 Hist1D::Hist1D(const std::vector<double>& xbins, const std::string& name, const std::string& title)
49 : Drawable(name, title) {
50 if (xbins.empty())
51 throw CG_ERROR("Hist1D") << "Number of bins must be strictly positive!";
52 buildFromBins(xbins);
53 }
54
56 : Histogram(oth),
57 Drawable(oth),
58 hist_(gsl_histogram_clone(oth.hist_.get())),
59 hist_w2_(gsl_histogram_clone(oth.hist_w2_.get())),
60 underflow_(oth.underflow_),
61 overflow_(oth.overflow_) {}
62
63 void Hist1D::buildFromBins(const std::vector<double>& bins) {
64 if (bins.size() < 1)
65 throw CG_ERROR("Hist1D:buildFromBins") << "Building a 1D histogram requires at least 1 bin.";
66 hist_.reset(gsl_histogram_alloc(bins.size() - 1));
67 CG_ASSERT(hist_);
68 if (auto ret = gsl_histogram_set_ranges(hist_.get(), bins.data(), bins.size()); ret != GSL_SUCCESS)
69 throw CG_ERROR("Hist1D:buildFromBins") << gsl_strerror(ret);
70 hist_w2_ = gsl_histogram_ptr(gsl_histogram_clone(hist_.get()));
71 CG_ASSERT(hist_w2_);
72 CG_DEBUG("Hist1D:buildFromBins") << "Booking a 1D histogram with " << s("bin", bins.size(), true) << " in range "
73 << bins << ".";
74 }
75
76 void Hist1D::buildFromRange(size_t num_bins, const Limits& range) {
77 if (range.range() <= 0.)
78 throw CG_ERROR("Hist1D:buildFromRange") << "Invalid range for binning: " << range << ".";
79 if (num_bins < 1)
80 throw CG_ERROR("Hist1D:buildFromRange") << "Building a 1D histogram requires at least 1 bin.";
81 hist_.reset(gsl_histogram_alloc(num_bins));
82 CG_ASSERT(hist_);
83 if (auto ret = gsl_histogram_set_ranges_uniform(hist_.get(), range.min(), range.max()); ret != GSL_SUCCESS)
84 throw CG_ERROR("Hist1D:buildFromRange") << gsl_strerror(ret);
85 hist_w2_ = gsl_histogram_ptr(gsl_histogram_clone(hist_.get()));
86 CG_ASSERT(hist_w2_);
87 CG_DEBUG("Hist1D:buildFromRange") << "Booking a 1D histogram with " << s("bin", num_bins, true) << " in range "
88 << range << ".";
89 }
90
92 CG_ASSERT(hist_);
93 CG_ASSERT(hist_w2_);
94 gsl_histogram_reset(hist_.get());
95 gsl_histogram_reset(hist_w2_.get());
96 }
97
98 void Hist1D::fill(double x, double weight) {
99 CG_ASSERT(hist_);
100 CG_ASSERT(hist_w2_);
101 { // reduce the scope of 'ret'
102 auto ret = gsl_histogram_accumulate(hist_.get(), x, weight);
103 if (ret == GSL_SUCCESS) {
104 if (auto ret2 = gsl_histogram_accumulate(hist_w2_.get(), x, weight * weight) != GSL_SUCCESS)
105 throw CG_ERROR("Hist1D:fill") << "(w2 histogram): " << gsl_strerror(ret2);
106 return;
107 }
108 if (ret != GSL_EDOM)
109 throw CG_ERROR("Hist1D:fill") << gsl_strerror(ret);
110 }
111 if (x < range().min())
112 underflow_ += weight;
113 else
114 overflow_ += weight;
115 }
116
117 void Hist1D::add(Hist1D oth, double scaling) {
118 CG_ASSERT(hist_);
119 CG_ASSERT(hist_w2_);
120 CG_ASSERT(oth.hist_);
121 CG_ASSERT(oth.hist_w2_);
122 if (oth.integral(true) == 0.) {
123 CG_WARNING("Hist1D:add") << "Other histogram is empty.";
124 return;
125 }
126 const double scl = std::pow(oth.integral(), -2);
127 oth.scale(scaling);
128 gsl_histogram_scale(oth.hist_w2_.get(), scl);
129 if (auto ret = gsl_histogram_add(hist_.get(), oth.hist_.get()); ret != GSL_SUCCESS)
130 throw CG_ERROR("Hist1D:add") << gsl_strerror(ret);
131 gsl_histogram_add(hist_w2_.get(), oth.hist_w2_.get());
132 underflow_ += oth.underflow_;
133 overflow_ += oth.overflow_;
134 }
135
136 void Hist1D::scale(double scaling) {
137 CG_ASSERT(hist_);
138 if (auto ret = gsl_histogram_scale(hist_.get(), scaling); ret != GSL_SUCCESS)
139 throw CG_ERROR("Hist1D:scale") << gsl_strerror(ret);
140 gsl_histogram_scale(hist_w2_.get(), scaling * scaling);
141 underflow_ *= scaling;
142 overflow_ *= scaling;
143 }
144
146 axis_t axis;
147 for (size_t bin = 0; bin < nbins(); ++bin) {
148 const auto& range_i = binRange(bin);
149 axis[coord_t{range_i.x(0.5), 0.5 * range_i.range(), format("[%7.2f,%7.2f)", range_i.min(), range_i.max())}] =
150 value(bin);
151 }
152 return axis;
153 }
154
155 size_t Hist1D::nbins() const {
156 CG_ASSERT(hist_);
157 return gsl_histogram_bins(hist_.get());
158 }
159
161 CG_ASSERT(hist_);
162 return Limits{gsl_histogram_min(hist_.get()), gsl_histogram_max(hist_.get())};
163 }
164
165 Limits Hist1D::binRange(size_t bin) const {
166 CG_ASSERT(hist_);
168 if (auto ret = gsl_histogram_get_range(hist_.get(), bin, &range.min(), &range.max()); ret != GSL_SUCCESS)
169 throw CG_ERROR("Hist1D:binRange") << "Bin " << bin << ": " << gsl_strerror(ret);
170 return range;
171 }
172
173 std::vector<double> Hist1D::bins(BinMode mode) const {
174 const auto bins = extractBins(mode, nbins(), std::bind(&Hist1D::binRange, this, std::placeholders::_1));
175 return std::vector<double>(bins.begin(), bins.end());
176 }
177
178 size_t Hist1D::bin(double x) const {
179 size_t bin_id;
180 if (auto ret = gsl_histogram_find(hist_.get(), x, &bin_id); ret != GSL_SUCCESS)
181 throw CG_ERROR("Hist1D:bin") << "Failed to retrieve bin index for value " << x << ": " << gsl_strerror(ret);
182 return bin_id;
183 }
184
185 std::vector<Value> Hist1D::values() const {
186 std::vector<Value> values;
187 for (size_t i = 0; i < nbins(); ++i)
188 values.emplace_back(value(i));
189 return values;
190 }
191
192 Value Hist1D::value(size_t bin) const {
193 CG_ASSERT(hist_);
194 CG_ASSERT(hist_w2_);
195 return Value{gsl_histogram_get(hist_.get(), bin), std::sqrt(gsl_histogram_get(hist_w2_.get(), bin))};
196 }
197
198 void Hist1D::setValue(size_t bin, Value val) {
199 const auto bin_centre = binRange(bin).x(0.5);
200 const auto val_old = value(bin);
201 gsl_histogram_accumulate(hist_.get(), bin_centre, val - val_old);
202 gsl_histogram_accumulate(hist_w2_.get(),
203 bin_centre,
204 val.uncertainty() * val.uncertainty() - val_old.uncertainty() * val_old.uncertainty());
205 }
206
207 double Hist1D::mean() const {
208 CG_ASSERT(hist_);
209 return gsl_histogram_mean(hist_.get());
210 }
211
212 double Hist1D::rms() const {
213 CG_ASSERT(hist_);
214 return gsl_histogram_sigma(hist_.get());
215 }
216
217 double Hist1D::minimum() const {
218 CG_ASSERT(hist_);
219 return gsl_histogram_min_val(hist_.get());
220 }
221
222 double Hist1D::maximum() const {
223 CG_ASSERT(hist_);
224 return gsl_histogram_max_val(hist_.get());
225 }
226
227 double Hist1D::integral(bool include_out_of_range) const {
228 CG_ASSERT(hist_);
229 auto integr = gsl_histogram_sum(hist_.get());
230 if (include_out_of_range)
231 integr += underflow_ + overflow_;
232 return integr;
233 }
234
235 double Hist1D::sample(RandomGenerator& rng) const {
236 if (!pdf_) {
237 pdf_.reset(gsl_histogram_pdf_alloc(nbins()));
238 if (const auto ret = gsl_histogram_pdf_init(pdf_.get(), hist_.get()); ret != GSL_SUCCESS)
239 throw CG_ERROR("Hist1D:sample") << "Failed to allocate the histogram PDF. GSL yielded: " << gsl_strerror(ret);
240 }
241 return gsl_histogram_pdf_sample(pdf_.get(), rng.uniform());
242 }
243
244 double Hist1D::chi2test(const Hist1D& oth, size_t& ndfval) const {
245 if (nbins() != oth.nbins())
246 return 0.;
247 double sum1{0.}, sum2{0.};
248 for (size_t i = 0; i < nbins(); ++i) {
249 const auto ru1 = value(i).relativeUncertainty(), ru2 = oth.value(i).relativeUncertainty();
250 sum1 += ru1 > 0. ? 1. / ru1 / ru1 : 0.;
251 sum2 += ru2 > 0. ? 1. / ru2 / ru2 : 0.;
252 }
253 if (sum1 == 0. || sum2 == 0.) {
254 ndfval = 0;
255 return 0.;
256 }
257 // perform the test
258 double chi2val = 0.;
259 ndfval = nbins();
260 for (size_t i = 0; i < nbins(); ++i) {
261 const auto ru1 = value(i).relativeUncertainty(), ru2 = oth.value(i).relativeUncertainty();
262 const auto cnt1 = ru1 > 0. ? 1. / ru1 / ru1 : 0., cnt2 = ru2 > 0. ? 1. / ru2 / ru2 : 0.;
263 if (cnt1 == 0. && cnt2 == 0.) {
264 --ndfval;
265 continue;
266 }
267 chi2val += std::pow(sum2 * cnt1 - sum1 * cnt2, 2) / (cnt1 + cnt2);
268 }
269 chi2val /= sum1 * sum2;
270 return chi2val;
271 }
272 } // namespace utils
273} // namespace cepgen
#define CG_FATAL(mod)
Definition Exception.h:61
#define CG_ERROR(mod)
Definition Exception.h:60
#define CG_ASSERT(assertion)
Definition Exception.h:62
#define CG_WARNING(mod)
Definition Message.h:228
#define CG_DEBUG(mod)
Definition Message.h:220
Validity interval for a variable.
Definition Limits.h:28
double x(double v) const
Find the [0,1] value scaled between minimum and maximum.
Definition Limits.cpp:97
bool valid() const
Is there a lower and upper limit?
Definition Limits.cpp:85
double range() const
Full variable range allowed.
Definition Limits.cpp:65
double min() const
Lower limit to apply on the variable.
Definition Limits.h:52
double max() const
Upper limit to apply on the variable.
Definition Limits.h:54
T get(const std::string &key, const T &def=default_arg< T >::get()) const
Get a parameter value.
A scalar value with its uncertainty.
Definition Value.h:26
double relativeUncertainty() const
Relative uncertainty around the central value.
Definition Value.cpp:28
double uncertainty() const
Absolute uncertainty around the central value.
Definition Value.h:35
A generic object which can be drawn in the standard output.
Definition Drawable.h:31
std::map< coord_t, Value > axis_t
Metadata for an axis (coordinates and bins value)
Definition Drawable.h:95
1D histogram container
Definition Histogram.h:72
void fill(double x, double weight=1.)
Increment the histogram with one value.
Definition Hist1D.cpp:98
double integral(bool=false) const override
Compute the histogram integral.
Definition Hist1D.cpp:227
size_t bin(double x) const
Retrieve the bin index for a x value.
Definition Hist1D.cpp:178
void setValue(size_t bin, Value value)
Set the value + uncertainty for one bin.
Definition Hist1D.cpp:198
void scale(double) override
Rescale all histogram bins by a constant factor.
Definition Hist1D.cpp:136
double mean() const
Compute the mean histogram value over full range.
Definition Hist1D.cpp:207
size_t nbins() const
Number of histogram bins.
Definition Hist1D.cpp:155
void add(Hist1D, double scaling=1.)
Bin-to-bin addition of another histogram to this one.
Definition Hist1D.cpp:117
axis_t axis() const
Axis content.
Definition Hist1D.cpp:145
double minimum() const override
Retrieve the maximum bin value.
Definition Hist1D.cpp:217
double rms() const
Compute the root-mean-square value over full range.
Definition Hist1D.cpp:212
std::vector< Value > values() const
Retrieve the value + uncertainty for all bins.
Definition Hist1D.cpp:185
Value value(size_t bin) const
Retrieve the value + uncertainty for one bin.
Definition Hist1D.cpp:192
Limits binRange(size_t bin) const
Range for a single bin.
Definition Hist1D.cpp:165
Hist1D(const ParametersList &)
Build a histogram from user-steeed parameters.
Definition Hist1D.cpp:31
std::vector< double > bins(BinMode) const
List of bins limits (nbins + 1 values if min-max, nbins values otherwise)
Definition Hist1D.cpp:173
double maximum() const override
Retrieve the minimum bin value.
Definition Hist1D.cpp:222
double chi2test(const Hist1D &, size_t &ndf) const
Perform a chi^2 test between two histograms.
Definition Hist1D.cpp:244
Limits range() const
Axis range.
Definition Hist1D.cpp:160
double sample(RandomGenerator &) const
Sample individual "events" from a distribution.
Definition Hist1D.cpp:235
void clear() override
Reset the histogram.
Definition Hist1D.cpp:91
Generic text-based plotting utility.
Definition Histogram.h:41
std::set< double > extractBins(BinMode mode, size_t num_bins, const std::function< Limits(size_t)> &bins_extractor) const
Extract the list of bin limits.
Definition Histogram.cpp:26
A random number generator.
virtual double uniform(double min=0., double max=1.)=0
std::string format(const std::string &fmt, Args... args)
Format a string using a printf style format descriptor.
Definition String.h:61
std::string s(const std::string &word, float num, bool show_number)
Add a trailing "s" when needed.
Definition String.cpp:228
Common namespace for this Monte Carlo generator.
Generic bin coordinate and its human-readable label.
Definition Drawable.h:87