OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
GSLHistogram.h
Go to the documentation of this file.
1//
2// Histogram implementation to replace GSL histogram
3//
4// Copyright (c) 2023, Paul Scherrer Institute, Villigen PSI, Switzerland
5// All rights reserved
6//
7// This file is part of OPAL.
8//
9// OPAL is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// You should have received a copy of the GNU General License
15// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
16//
17
18#ifndef OPAL_GSL_HISTOGRAM_HH
19#define OPAL_GSL_HISTOGRAM_HH
20
21#include <algorithm>
22#include <cmath>
23#include <cstdio>
24#include <stdexcept>
25#include <vector>
26
29// 1D Histogram
32public:
33 gsl_histogram() : n_(0), range_(), bin_(0) {}
34
38 void set_ranges_uniform(double xmin, double xmax) {
39 if (xmin >= xmax) {
40 throw std::invalid_argument("gsl_histogram: xmin must be < xmax");
41 }
42 range_.resize(n_ + 1);
43 double bin_width = (xmax - xmin) / n_;
44 for (size_t i = 0; i <= n_; ++i) {
45 range_[i] = xmin + i * bin_width;
46 }
47 }
48
52 void set_ranges(const double* range, size_t n) {
53 if (n != n_ + 1) {
54 throw std::invalid_argument("gsl_histogram: range size mismatch");
55 }
56 range_.assign(range, range + n);
57 }
58
61 void increment(double x) {
62 size_t bin = find_bin(x);
63 if (bin < bin_.size()) {
64 bin_[bin]++;
65 }
66 }
67
71 double get(size_t i) const {
72 if (i >= bin_.size()) {
73 throw std::out_of_range("gsl_histogram: bin index out of range");
74 }
75 return bin_[i];
76 }
77
80 size_t n() const { return n_; }
83 const std::vector<double>& range() const { return range_; }
86 const std::vector<double>& bin() const { return bin_; }
87
88 // Make members accessible for GSL compatibility functions
89 size_t n_;
90 std::vector<double> range_;
91 std::vector<double> bin_;
92
93private:
94 size_t find_bin(double x) const {
95 if (range_.empty()) return bin_.size();
96
97 // Handle out-of-range values
98 if (x < range_[0]) return bin_.size();
99 if (x >= range_.back()) return bin_.size();
100
101 // Binary search for the bin
102 auto it = std::upper_bound(range_.begin(), range_.end(), x);
103 return std::distance(range_.begin(), it) - 1;
104 }
105};
106
107// GSL-compatible functions for 1D histogram
112 gsl_histogram* h = new gsl_histogram();
113 h->n_ = n;
114 h->bin_.resize(n, 0.0);
115 return h;
116}
117
122inline void gsl_histogram_set_ranges_uniform(gsl_histogram* h, double xmin, double xmax) {
123 h->set_ranges_uniform(xmin, xmax);
124}
125
129inline void gsl_histogram_increment(gsl_histogram* h, double x) { h->increment(x); }
130
135inline double gsl_histogram_get(const gsl_histogram* h, size_t i) { return h->get(i); }
136
139inline void gsl_histogram_free(gsl_histogram* h) { delete h; }
140
144inline size_t gsl_histogram_bins(const gsl_histogram* h) { return h->n(); }
145
152inline int gsl_histogram_get_range(const gsl_histogram* h, size_t i, double* lower, double* upper) {
153 if (i >= h->n()) {
154 return -1; // Error: bin index out of range
155 }
156 if (h->range().empty() || i >= h->range().size() - 1) {
157 return -1;
158 }
159 *lower = h->range()[i];
160 *upper = h->range()[i + 1];
161 return 0;
162}
163
169inline int gsl_histogram_set_ranges(gsl_histogram* h, const double* range, size_t n) {
170 h->set_ranges(range, n);
171 return 0;
172}
173
174// 2D Histogram
177public:
178 gsl_histogram2d() : nx_(0), ny_(0), xrange_(), yrange_(), bin_() {}
179
186 void set_ranges_uniform(double xmin, double xmax, double ymin, double ymax) {
187 if (xmin >= xmax || ymin >= ymax) {
188 throw std::invalid_argument("gsl_histogram2d: invalid range");
189 }
190 xrange_.resize(nx_ + 1);
191 yrange_.resize(ny_ + 1);
192 double xbin_width = (xmax - xmin) / nx_;
193 double ybin_width = (ymax - ymin) / ny_;
194 for (size_t i = 0; i <= nx_; ++i) {
195 xrange_[i] = xmin + i * xbin_width;
196 }
197 for (size_t i = 0; i <= ny_; ++i) {
198 yrange_[i] = ymin + i * ybin_width;
199 }
200 }
201
206 void accumulate(double x, double y, double weight) {
207 size_t i = find_x_bin(x);
208 size_t j = find_y_bin(y);
209 if (i < nx_ && j < ny_) {
210 bin_[i * ny_ + j] += weight;
211 }
212 }
213
217 void increment(double x, double y) { accumulate(x, y, 1.0); }
218
224 void set_ranges(const double* xrange, size_t nx, const double* yrange, size_t ny) {
225 if (nx != nx_ + 1 || ny != ny_ + 1) {
226 throw std::invalid_argument("gsl_histogram2d: range size mismatch");
227 }
228 xrange_.assign(xrange, xrange + nx);
229 yrange_.assign(yrange, yrange + ny);
230 }
231
236 double get(size_t i, size_t j) const {
237 if (i >= nx_ || j >= ny_) {
238 throw std::out_of_range("gsl_histogram2d: bin index out of range");
239 }
240 return bin_[i * ny_ + j];
241 }
242
245 size_t nx() const { return nx_; }
248 size_t ny() const { return ny_; }
251 const std::vector<double>& xrange() const { return xrange_; }
254 const std::vector<double>& yrange() const { return yrange_; }
257 const std::vector<double>& bin() const { return bin_; }
258
259 // Make members accessible for GSL compatibility functions
260 size_t nx_, ny_;
261 std::vector<double> xrange_;
262 std::vector<double> yrange_;
263 std::vector<double> bin_;
264
265private:
266 size_t find_x_bin(double x) const {
267 if (xrange_.empty() || x < xrange_[0] || x >= xrange_.back()) {
268 return nx_;
269 }
270 auto it = std::upper_bound(xrange_.begin(), xrange_.end(), x);
271 return std::distance(xrange_.begin(), it) - 1;
272 }
273
274 size_t find_y_bin(double y) const {
275 if (yrange_.empty() || y < yrange_[0] || y >= yrange_.back()) {
276 return ny_;
277 }
278 auto it = std::upper_bound(yrange_.begin(), yrange_.end(), y);
279 return std::distance(yrange_.begin(), it) - 1;
280 }
281};
282
283// 2D Histogram PDF for sampling
286public:
288
291 void init(const gsl_histogram2d* h) {
292 nx_ = h->nx();
293 ny_ = h->ny();
294 xrange_ = h->xrange();
295 yrange_ = h->yrange();
296
297 const auto& bin = h->bin();
298 double total = 0.0;
299 for (double val : bin) {
300 total += val;
301 }
302
303 pdf_.resize(bin.size());
304 if (total > 0) {
305 for (size_t i = 0; i < bin.size(); ++i) {
306 pdf_[i] = bin[i] / total;
307 }
308 }
309
310 // Compute cumulative distribution for sampling
311 cumsum_.resize(pdf_.size());
312 double cum = 0.0;
313 for (size_t i = 0; i < pdf_.size(); ++i) {
314 cum += pdf_[i];
315 cumsum_[i] = cum;
316 }
317 }
318
324 void sample(double u, double v, double* x, double* y) {
325 // Find bin using u (for x) and v (for y within that x slice)
326 size_t bin_idx = 0;
327 double target = u * cumsum_.back();
328 for (size_t i = 0; i < cumsum_.size(); ++i) {
329 if (cumsum_[i] >= target) {
330 bin_idx = i;
331 break;
332 }
333 }
334
335 size_t i = bin_idx / ny_;
336 size_t j = bin_idx % ny_;
337
338 if (i < nx_ && j < ny_) {
339 double xmin = xrange_[i];
340 double xmax = xrange_[i + 1];
341 double ymin = yrange_[j];
342 double ymax = yrange_[j + 1];
343 *x = xmin + (xmax - xmin) * v;
344 *y = ymin + (ymax - ymin) * u;
345 } else {
346 *x = 0.0;
347 *y = 0.0;
348 }
349 }
350
351private:
352 size_t nx_, ny_;
353 std::vector<double> xrange_;
354 std::vector<double> yrange_;
355 std::vector<double> pdf_;
356 std::vector<double> cumsum_;
357};
358
359// GSL-compatible functions for 2D histogram
364inline gsl_histogram2d* gsl_histogram2d_alloc(size_t nx, size_t ny) {
366 h->nx_ = nx;
367 h->ny_ = ny;
368 h->bin_.resize(nx * ny, 0.0);
369 return h;
370}
371
379 gsl_histogram2d* h, double xmin, double xmax, double ymin, double ymax) {
380 h->set_ranges_uniform(xmin, xmax, ymin, ymax);
381}
382
391 gsl_histogram2d* h, const double* xrange, size_t nx, const double* yrange, size_t ny) {
392 h->set_ranges(xrange, nx, yrange, ny);
393 return 0;
394}
395
399inline size_t gsl_histogram2d_nx(const gsl_histogram2d* h) { return h->nx(); }
400
404inline size_t gsl_histogram2d_ny(const gsl_histogram2d* h) { return h->ny(); }
405
410inline void gsl_histogram2d_increment(gsl_histogram2d* h, double x, double y) {
411 h->increment(x, y);
412}
413
419inline double gsl_histogram2d_get(const gsl_histogram2d* h, size_t i, size_t j) {
420 return h->get(i, j);
421}
422
428inline void gsl_histogram2d_accumulate(gsl_histogram2d* h, double x, double y, double weight) {
429 h->accumulate(x, y, weight);
430}
431
438 FILE* fh, const gsl_histogram2d* h, const char* xformat, const char* yformat) {
439 fprintf(fh, "# xrange: %zu bins from %g to %g\n", h->nx(), h->xrange()[0], h->xrange().back());
440 fprintf(fh, "# yrange: %zu bins from %g to %g\n", h->ny(), h->yrange()[0], h->yrange().back());
441 const auto& bin = h->bin();
442 for (size_t i = 0; i < h->nx(); ++i) {
443 for (size_t j = 0; j < h->ny(); ++j) {
444 fprintf(fh, xformat, h->xrange()[i]);
445 fprintf(fh, " ");
446 fprintf(fh, yformat, h->yrange()[j]);
447 fprintf(fh, " %g\n", bin[i * h->ny() + j]);
448 }
449 }
450}
451
454inline void gsl_histogram2d_free(gsl_histogram2d* h) { delete h; }
455
460inline gsl_histogram2d_pdf* gsl_histogram2d_pdf_alloc(size_t /* nx */, size_t /* ny */) {
461 return new gsl_histogram2d_pdf();
462}
463
468 p->init(h);
469}
470
478 const gsl_histogram2d_pdf* p, double u, double v, double* x, double* y) {
479 const_cast<gsl_histogram2d_pdf*>(p)->sample(u, v, x, y);
480}
481
485
486#endif // OPAL_GSL_HISTOGRAM_HH
gsl_histogram2d * gsl_histogram2d_alloc(size_t nx, size_t ny)
Allocate a 2D histogram with nx by ny bins.
void gsl_histogram2d_pdf_free(gsl_histogram2d_pdf *p)
Free a 2D histogram PDF.
void gsl_histogram2d_fprintf(FILE *fh, const gsl_histogram2d *h, const char *xformat, const char *yformat)
Print the 2D histogram in a simple text table.
void gsl_histogram2d_increment(gsl_histogram2d *h, double x, double y)
Increment the bin containing x,y by 1.
void gsl_histogram_free(gsl_histogram *h)
Free a histogram allocated by gsl_histogram_alloc.
int gsl_histogram2d_set_ranges(gsl_histogram2d *h, const double *xrange, size_t nx, const double *yrange, size_t ny)
Set explicit x/y bin edges.
int gsl_histogram_get_range(const gsl_histogram *h, size_t i, double *lower, double *upper)
Get the range for a bin.
void gsl_histogram_increment(gsl_histogram *h, double x)
Increment the bin containing x by 1.
void gsl_histogram2d_accumulate(gsl_histogram2d *h, double x, double y, double weight)
Accumulate a weighted sample into the 2D histogram.
double gsl_histogram2d_get(const gsl_histogram2d *h, size_t i, size_t j)
Get the bin count for index .
void gsl_histogram2d_pdf_init(gsl_histogram2d_pdf *p, const gsl_histogram2d *h)
Initialize a PDF from a histogram.
size_t gsl_histogram2d_nx(const gsl_histogram2d *h)
Number of x bins.
void gsl_histogram_set_ranges_uniform(gsl_histogram *h, double xmin, double xmax)
Set uniform bin edges over .
void gsl_histogram2d_pdf_sample(const gsl_histogram2d_pdf *p, double u, double v, double *x, double *y)
Sample from the PDF using two uniform variates.
size_t gsl_histogram2d_ny(const gsl_histogram2d *h)
Number of y bins.
gsl_histogram * gsl_histogram_alloc(size_t n)
Allocate a 1D histogram with n bins.
int gsl_histogram_set_ranges(gsl_histogram *h, const double *range, size_t n)
Set explicit bin edges for a histogram.
void gsl_histogram2d_set_ranges_uniform(gsl_histogram2d *h, double xmin, double xmax, double ymin, double ymax)
Set uniform x/y bin edges.
double gsl_histogram_get(const gsl_histogram *h, size_t i)
Get the bin count for index i.
gsl_histogram2d_pdf * gsl_histogram2d_pdf_alloc(size_t, size_t)
Allocate a 2D histogram PDF.
void gsl_histogram2d_free(gsl_histogram2d *h)
Free a 2D histogram.
size_t gsl_histogram_bins(const gsl_histogram *h)
Number of bins in the histogram.
2D PDF derived from histogram for sampling.
std::vector< double > xrange_
void sample(double u, double v, double *x, double *y)
Sample given two uniform variates.
std::vector< double > pdf_
std::vector< double > yrange_
void init(const gsl_histogram2d *h)
Initialize a PDF from a 2D histogram.
std::vector< double > cumsum_
2D histogram with explicit x/y bin edges.
double get(size_t i, size_t j) const
Get the bin count for index .
const std::vector< double > & bin() const
Bin count array.
std::vector< double > xrange_
void set_ranges(const double *xrange, size_t nx, const double *yrange, size_t ny)
Set explicit x/y bin edges.
void set_ranges_uniform(double xmin, double xmax, double ymin, double ymax)
Set uniform x/y bin edges over and .
size_t find_x_bin(double x) const
const std::vector< double > & yrange() const
Y-axis bin edges.
std::vector< double > yrange_
size_t nx() const
Number of x bins.
size_t find_y_bin(double y) const
void increment(double x, double y)
Increment the bin containing x,y by 1.
std::vector< double > bin_
size_t ny() const
Number of y bins.
const std::vector< double > & xrange() const
X-axis bin edges.
void accumulate(double x, double y, double weight)
Accumulate a weighted sample into the 2D histogram.
1D histogram with explicit bin edges.
void set_ranges_uniform(double xmin, double xmax)
Set uniform bin edges over .
void set_ranges(const double *range, size_t n)
Set bin edges from an explicit array.
std::vector< double > bin_
const std::vector< double > & range() const
Bin edge array.
void increment(double x)
Increment the bin containing x by 1.
std::vector< double > range_
const std::vector< double > & bin() const
Bin count array.
double get(size_t i) const
Get the bin count for index i.
size_t n() const
Number of bins.
size_t find_bin(double x) const