cepgen is hosted by Hepforge, IPPP Durham
CepGen 1.2.5
Central exclusive processes event generator
Loading...
Searching...
No Matches
Hist2D.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 const auto &xbins = params.get<std::vector<double> >("xbins"), &ybins = params.get<std::vector<double> >("ybins");
34 const auto &xrange = params.get<Limits>("xrange"), &yrange = params.get<Limits>("yrange");
35 const auto &nbinsx = params.get<int>("nbinsX"), &nbinsy = params.get<int>("nbinsY");
36 if (xbins.size() > 1 && ybins.size() > 1)
37 buildFromBins(xbins, ybins);
38 else if (xrange.valid() && yrange.valid() && nbinsx > 1 && nbinsy > 1)
39 buildFromRange(nbinsx, xrange, nbinsy, yrange);
40 else
41 throw CG_FATAL("Hist2D") << "Failed to build a 2D histogram with user parameters: " << params << ".";
42 }
43
44 Hist2D::Hist2D(size_t num_bins_x,
45 const Limits& xrange,
46 size_t num_bins_y,
47 const Limits& yrange,
48 const std::string& name,
49 const std::string& title)
50 : Drawable(name, title) {
51 if (num_bins_x == 0 || num_bins_y == 0)
52 throw CG_ERROR("Hist1D") << "Number of bins must be strictly positive!";
53 buildFromRange(num_bins_x, xrange, num_bins_y, yrange);
54 }
55
56 Hist2D::Hist2D(const std::vector<double>& xbins,
57 const std::vector<double>& ybins,
58 const std::string& name,
59 const std::string& title)
60 : Drawable(name, title) {
61 if (xbins.empty() || ybins.empty())
62 throw CG_ERROR("Hist1D") << "Number of bins must be strictly positive!";
63 buildFromBins(xbins, ybins);
64 }
65
67 : Histogram(oth),
68 Drawable(oth),
69 hist_(gsl_histogram2d_clone(oth.hist_.get())),
70 hist_w2_(gsl_histogram2d_clone(oth.hist_w2_.get())),
71 out_of_range_values_(oth.out_of_range_values_) {}
72
73 void Hist2D::buildFromBins(const std::vector<double>& xbins, const std::vector<double>& ybins) {
74 if (xbins.size() < 1 || ybins.size() < 1)
75 throw CG_ERROR("Hist2D:buildFromBins") << "Building a 2D histogram requires at least 1x1 bin.";
76 hist_.reset(gsl_histogram2d_alloc(xbins.size() - 1, ybins.size() - 1));
77 CG_ASSERT(hist_);
78 if (auto ret = gsl_histogram2d_set_ranges(hist_.get(), xbins.data(), xbins.size(), ybins.data(), ybins.size());
79 ret != GSL_SUCCESS)
80 throw CG_ERROR("Hist2D:buildFromBins") << gsl_strerror(ret);
81 hist_w2_ = gsl_histogram2d_ptr(gsl_histogram2d_clone(hist_.get()));
82 CG_ASSERT(hist_w2_);
83 CG_DEBUG("Hist2D:buildFromBins") << "Booking a 2D correlation plot with " << s("bin", xbins.size(), true)
84 << " in range x=" << xbins << " and " << s("bin", ybins.size(), true)
85 << " in range y=" << ybins << ".";
86 }
87
88 void Hist2D::buildFromRange(size_t num_bins_x, const Limits& xrange, size_t num_bins_y, const Limits& yrange) {
89 if (xrange.range() <= 0. || yrange.range() <= 0.)
90 throw CG_ERROR("Hist2D:buildFromRange") << "Invalid range for binning: " << xrange << "x" << yrange << ".";
91 if (num_bins_x < 1 || num_bins_y < 1)
92 throw CG_ERROR("Hist2D:buildFromRange") << "Building a 2D histogram requires at least 1x1 bin.";
93 hist_.reset(gsl_histogram2d_alloc(num_bins_x, num_bins_y));
94 CG_ASSERT(hist_);
95 if (auto ret =
96 gsl_histogram2d_set_ranges_uniform(hist_.get(), xrange.min(), xrange.max(), yrange.min(), yrange.max());
97 ret != GSL_SUCCESS)
98 throw CG_ERROR("Hist2D:buildFromRange") << gsl_strerror(ret);
99 hist_w2_ = gsl_histogram2d_ptr(gsl_histogram2d_clone(hist_.get()));
100 CG_ASSERT(hist_w2_);
101 CG_DEBUG("Hist2D:buildFromRange") << "Booking a 2D correlation plot with " << s("bin", num_bins_x, true)
102 << " in range " << xrange << " and " << s("bin", num_bins_y, true)
103 << " in range " << yrange << ".";
104 }
105
107 CG_ASSERT(hist_);
108 CG_ASSERT(hist_w2_);
109 gsl_histogram2d_reset(hist_.get());
110 gsl_histogram2d_reset(hist_w2_.get());
111 }
112
113 void Hist2D::fill(double x, double y, double weight) {
114 CG_ASSERT(hist_);
115 CG_ASSERT(hist_w2_);
116 { // reduce the scope of 'ret'
117 auto ret = gsl_histogram2d_accumulate(hist_.get(), x, y, weight);
118 if (ret == GSL_SUCCESS) {
119 gsl_histogram2d_accumulate(hist_w2_.get(), x, y, weight * weight);
120 return;
121 }
122 if (ret != GSL_EDOM)
123 throw CG_ERROR("Hist2D:fill") << gsl_strerror(ret);
124 }
125 const auto &xrng = rangeX(), &yrng = rangeY();
126 if (xrng.contains(x)) {
127 if (y < yrng.min())
128 out_of_range_values_[contents_t::IN_LT] += weight;
129 else
130 out_of_range_values_[contents_t::IN_GT] += weight;
131 } else if (x < xrng.min()) {
132 if (yrng.contains(y))
133 out_of_range_values_[contents_t::LT_IN] += weight;
134 else if (y < yrng.min())
135 out_of_range_values_[contents_t::LT_LT] += weight;
136 else
137 out_of_range_values_[contents_t::LT_GT] += weight;
138 } else {
139 if (yrng.contains(y))
140 out_of_range_values_[contents_t::GT_IN] += weight;
141 else if (y < yrng.min())
142 out_of_range_values_[contents_t::GT_LT] += weight;
143 else
144 out_of_range_values_[contents_t::GT_GT] += weight;
145 }
146 }
147
148 void Hist2D::add(Hist2D oth, double scaling) {
149 CG_ASSERT(hist_);
150 CG_ASSERT(hist_w2_);
151 CG_ASSERT(oth.hist_);
152 CG_ASSERT(oth.hist_w2_);
153 if (oth.integral(true) == 0.) {
154 CG_WARNING("Hist1D:add") << "Other histogram is empty.";
155 return;
156 }
157 const double scl = std::pow(oth.integral(), -2);
158 oth.scale(scaling);
159 gsl_histogram2d_scale(oth.hist_w2_.get(), scl);
160 if (auto ret = gsl_histogram2d_add(hist_.get(), oth.hist_.get()); ret != GSL_SUCCESS)
161 throw CG_ERROR("Hist2D:add") << gsl_strerror(ret);
162 gsl_histogram2d_add(hist_w2_.get(), oth.hist_w2_.get());
163 out_of_range_values_ += scaling * oth.out_of_range_values_;
164 }
165
166 void Hist2D::scale(double scaling) {
167 CG_ASSERT(hist_);
168 if (auto ret = gsl_histogram2d_scale(hist_.get(), scaling); ret != GSL_SUCCESS)
169 throw CG_ERROR("Hist2D:scale") << gsl_strerror(ret);
170 gsl_histogram2d_scale(hist_w2_.get(), scaling * scaling);
171 }
172
173 size_t Hist2D::nbinsX() const {
174 CG_ASSERT(hist_);
175 return gsl_histogram2d_nx(hist_.get());
176 }
177
179 CG_ASSERT(hist_);
180 return Limits{gsl_histogram2d_xmin(hist_.get()), gsl_histogram2d_xmax(hist_.get())};
181 }
182
183 Limits Hist2D::binRangeX(size_t bin) const {
184 CG_ASSERT(hist_);
185 Limits range;
186 if (auto ret = gsl_histogram2d_get_xrange(hist_.get(), bin, &range.min(), &range.max()); ret != GSL_SUCCESS)
187 throw CG_ERROR("Hist1D:binRange") << "Bin " << bin << ": " << gsl_strerror(ret);
188 return range;
189 }
190
191 std::vector<double> Hist2D::binsX(BinMode mode) const {
192 const auto bins = extractBins(mode, nbinsX(), std::bind(&Hist2D::binRangeX, this, std::placeholders::_1));
193 return std::vector<double>(bins.begin(), bins.end());
194 }
195
196 size_t Hist2D::nbinsY() const {
197 CG_ASSERT(hist_);
198 return gsl_histogram2d_ny(hist_.get());
199 }
200
202 CG_ASSERT(hist_);
203 return Limits{gsl_histogram2d_ymin(hist_.get()), gsl_histogram2d_ymax(hist_.get())};
204 }
205
206 Limits Hist2D::binRangeY(size_t bin) const {
207 CG_ASSERT(hist_);
208 Limits range;
209 if (auto ret = gsl_histogram2d_get_yrange(hist_.get(), bin, &range.min(), &range.max()); ret != GSL_SUCCESS)
210 throw CG_ERROR("Hist1D:binRange") << "Bin " << bin << ": " << gsl_strerror(ret);
211 return range;
212 }
213
214 std::vector<double> Hist2D::binsY(BinMode mode) const {
215 const auto bins = extractBins(mode, nbinsY(), std::bind(&Hist2D::binRangeY, this, std::placeholders::_1));
216 return std::vector<double>(bins.begin(), bins.end());
217 }
218
219 std::pair<size_t, size_t> Hist2D::bin(double x, double y) const {
220 std::pair<size_t, size_t> bin_ids;
221 if (auto ret = gsl_histogram2d_find(hist_.get(), x, y, &bin_ids.first, &bin_ids.second); ret != GSL_SUCCESS)
222 throw CG_ERROR("Hist2D:bin") << "Failed to retrieve bin index for values (" << x << ", " << y
223 << "): " << gsl_strerror(ret);
224 return bin_ids;
225 }
226
227 Value Hist2D::value(size_t bin_x, size_t bin_y) const {
228 CG_ASSERT(hist_);
229 return Value{gsl_histogram2d_get(hist_.get(), bin_x, bin_y),
230 std::sqrt(gsl_histogram2d_get(hist_w2_.get(), bin_x, bin_y))};
231 }
232
233 void Hist2D::setValue(size_t bin_x, size_t bin_y, Value val) {
234 const auto bin_centre_x = binRangeX(bin_x).x(0.5), bin_centre_y = binRangeY(bin_y).x(0.5);
235 const auto val_old = value(bin_x, bin_y);
236 gsl_histogram2d_accumulate(hist_.get(), bin_centre_x, bin_centre_y, val - val_old);
237 gsl_histogram2d_accumulate(hist_w2_.get(),
238 bin_centre_x,
239 bin_centre_y,
240 val.uncertainty() * val.uncertainty() - val_old.uncertainty() * val_old.uncertainty());
241 }
242
243 double Hist2D::meanX() const {
244 CG_ASSERT(hist_);
245 return gsl_histogram2d_xmean(hist_.get());
246 }
247
248 double Hist2D::rmsX() const {
249 CG_ASSERT(hist_);
250 return gsl_histogram2d_xsigma(hist_.get());
251 }
252
253 double Hist2D::meanY() const {
254 CG_ASSERT(hist_);
255 return gsl_histogram2d_ymean(hist_.get());
256 }
257
258 double Hist2D::rmsY() const {
259 CG_ASSERT(hist_);
260 return gsl_histogram2d_ysigma(hist_.get());
261 }
262
263 double Hist2D::minimum() const {
264 CG_ASSERT(hist_);
265 return gsl_histogram2d_min_val(hist_.get());
266 }
267
268 double Hist2D::maximum() const {
269 CG_ASSERT(hist_);
270 return gsl_histogram2d_max_val(hist_.get());
271 }
272
273 double Hist2D::integral(bool include_out_of_range) const {
274 CG_ASSERT(hist_);
275 auto integr = gsl_histogram2d_sum(hist_.get());
276 if (include_out_of_range)
277 integr += out_of_range_values_.total();
278 return integr;
279 }
280
281 size_t Hist2D::contents_t::total() const { return std::accumulate(begin(), end(), 0); }
282
283 Hist2D::contents_t operator*(double scaling, const Hist2D::contents_t& oth) {
284 Hist2D::contents_t tmp = oth;
285 std::transform(tmp.begin(), tmp.end(), tmp.begin(), [&scaling](const auto& bin) { return bin * scaling; });
286 return tmp;
287 }
288
290 for (size_t i = 0; i < num_content; ++i)
291 operator[](i) += oth.at(i);
292 return *this;
293 }
294
295 std::ostream& operator<<(std::ostream& os, const Hist2D::contents_t& cnt) {
296 return os << format(
297 "%10zu | %10zu | %10zu\n"
298 "%10zu | %10s | %10zu\n"
299 "%10zu | %10zu | %10zu",
304 "-",
309 }
310
311 std::pair<double, double> Hist2D::sample(RandomGenerator& rng) const {
312 if (!pdf_) {
313 pdf_.reset(gsl_histogram2d_pdf_alloc(nbinsX(), nbinsY()));
314 if (const auto ret = gsl_histogram2d_pdf_init(pdf_.get(), hist_.get()); ret != GSL_SUCCESS)
315 throw CG_FATAL("Hist2D:sample") << "Failed to allocate the histogram PDF. GSL yielded: " << gsl_strerror(ret);
316 }
317 std::pair<double, double> value;
318 const auto xi = rng.uniform(), yi = rng.uniform();
319 if (const auto ret = gsl_histogram2d_pdf_sample(pdf_.get(), xi, yi, &value.first, &value.second);
320 ret != GSL_SUCCESS)
321 throw CG_FATAL("Hist2D:sample") << "Failed to sample point (" << xi << ", " << yi
322 << ") from the histogram PDF. GSL yielded: " << gsl_strerror(ret);
323 return value;
324 }
325 } // namespace utils
326} // 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
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 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
2D histogram container
Definition Histogram.h:146
Limits binRangeX(size_t bin) const
Range for a single x-axis bin.
Definition Hist2D.cpp:183
double integral(bool=false) const override
Compute the histogram integral.
Definition Hist2D.cpp:273
std::pair< size_t, size_t > bin(double x, double y) const
Retrieve the bin indices for a (x, y) value.
Definition Hist2D.cpp:219
Hist2D(const ParametersList &)
Build a histogram from user-steeed parameters.
Definition Hist2D.cpp:31
void scale(double) override
Rescale all histogram bins by a constant factor.
Definition Hist2D.cpp:166
Limits rangeX() const
x-axis range
Definition Hist2D.cpp:178
double minimum() const override
Retrieve the maximum bin value.
Definition Hist2D.cpp:263
void add(Hist2D, double scaling=1.)
Bin-by-bin addition of another histogram to this one.
Definition Hist2D.cpp:148
void fill(double x, double y, double weight=1.)
Fill the histogram with one value.
Definition Hist2D.cpp:113
size_t nbinsX() const
Number of x-axis bins.
Definition Hist2D.cpp:173
std::vector< double > binsX(BinMode) const
List of x-bins limits (nbinsX + 1 values if min-max, nbins values otherwise)
Definition Hist2D.cpp:191
Value value(size_t bin_x, size_t bin_y) const
Retrieve the value + uncertainty for one bin.
Definition Hist2D.cpp:227
double rmsX() const
Compute the root-mean-square value over full x-axis range.
Definition Hist2D.cpp:248
double meanY() const
Compute the mean histogram value over full y-axis range.
Definition Hist2D.cpp:253
double rmsY() const
Compute the root-mean-square value over full y-axis range.
Definition Hist2D.cpp:258
double maximum() const override
Retrieve the minimum bin value.
Definition Hist2D.cpp:268
size_t nbinsY() const
Number of y-axis bins.
Definition Hist2D.cpp:196
Limits binRangeY(size_t bin) const
Range for a single y-axis bin.
Definition Hist2D.cpp:206
std::pair< double, double > sample(RandomGenerator &) const
Sample individual "events" from a distribution.
Definition Hist2D.cpp:311
double meanX() const
Compute the mean histogram value over full x-axis range.
Definition Hist2D.cpp:243
void clear() override
Reset the histogram.
Definition Hist2D.cpp:106
std::vector< double > binsY(BinMode) const
List of y-bins limits (nbinsY + 1 values if min-max, nbins values otherwise)
Definition Hist2D.cpp:214
void setValue(size_t bin_x, size_t bin_y, Value value)
Set the value + uncertainty for one bin.
Definition Hist2D.cpp:233
Limits rangeY() const
y-axis range
Definition Hist2D.cpp:201
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::ostream & operator<<(std::ostream &os, const Drawer::Mode &mode)
Definition Drawer.cpp:37
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
Hist2D::contents_t operator*(double scaling, const Hist2D::contents_t &oth)
Definition Hist2D.cpp:283
Common namespace for this Monte Carlo generator.
contents_t & operator+=(const contents_t &)
Definition Hist2D.cpp:289