Skip to content

Commit 469c877

Browse files
committed
Fix merging log space Histogram1D
1 parent 5f534e2 commit 469c877

File tree

3 files changed

+111
-40
lines changed

3 files changed

+111
-40
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
- Fixed error parsing SelectedEventFunctionParams from JSON.
13+
- Fixed error merging log space Histogram1D which resulted in infinite loops.
1314

1415

1516
## [2.0a3] - 2024-12-11

include/casm/monte/sampling/SelectedEventFunctions.hh

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ class Histogram1D {
316316
/// \brief Insert a value into the histogram, with an optional weight
317317
void insert(double value, double weight = 1.0);
318318

319+
/// \brief Insert the log of a value into a log space histogram directly,
320+
/// with an optional weight.
321+
void insert_log_value(double log_value, double weight = 1.0);
322+
319323
/// \brief Return the coordinates of the beginning of each bin range
320324
std::vector<double> bin_coords() const;
321325

@@ -330,6 +334,9 @@ class Histogram1D {
330334
void merge(Histogram1D const &other);
331335

332336
private:
337+
/// \brief Insert a value into the histogram, with an optional weight
338+
void _insert(double value, double weight);
339+
333340
/// \brief Reset histogram bins if this is the first value being added,
334341
/// or if `value` is less than `begin`
335342
void _reset_bins(double value);
@@ -385,12 +392,7 @@ class PartitionedHistogram1D {
385392
/// \brief Constructor
386393
PartitionedHistogram1D(std::vector<std::string> const &_partion_names,
387394
double _initial_begin, double _bin_width, bool _is_log,
388-
Index _max_size = 10000)
389-
: m_partition_names(_partion_names),
390-
m_histograms(
391-
_partion_names.size(),
392-
Histogram1D(_initial_begin, _bin_width, _is_log, _max_size)),
393-
m_up_to_date(false) {}
395+
Index _max_size = 10000);
394396

395397
/// \brief Return the names of the partitions
396398
std::vector<std::string> const &partition_names() const {
@@ -433,19 +435,7 @@ class PartitionedHistogram1D {
433435

434436
private:
435437
/// \brief Make the combined histogram from the partitioned histograms
436-
void _make_combined_histogram() const {
437-
m_combined_histogram = combine(m_histograms);
438-
std::vector<double> bin_coords = m_combined_histogram->bin_coords();
439-
if (!bin_coords.empty()) {
440-
auto &hist = const_cast<std::vector<Histogram1D> &>(m_histograms);
441-
for (Index i = 0; i < hist.size(); ++i) {
442-
hist[i].insert(bin_coords.front(), 0.0);
443-
hist[i].insert(bin_coords.back(), 0.0);
444-
}
445-
}
446-
447-
m_up_to_date = true;
448-
}
438+
void _make_combined_histogram() const;
449439

450440
/// The names of the partitions
451441
std::vector<std::string> m_partition_names;

src/casm/monte/sampling/SelectedEventFunctions.cc

Lines changed: 101 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -210,31 +210,41 @@ Histogram1D::Histogram1D(double _initial_begin, double _bin_width, bool _is_log,
210210
m_out_of_range_count(0.0) {}
211211

212212
/// \brief Insert a value into the histogram, with an optional weight
213+
///
214+
/// Notes:
215+
/// - This function takes the "real value" being inserted into the histogram,
216+
/// whether the histogram is in linear or log space.
217+
///
218+
/// \param log_value The "real value" being inserted into the histogram. If the
219+
/// histogram is in log space, then the logarithm (std::log10) of `value` is
220+
/// evaluated and inserted.
221+
/// \param weight The weight to assign to the value
222+
213223
void Histogram1D::insert(double value, double weight) {
214224
if (m_is_log) {
215-
value = std::log10(value);
216-
}
217-
if (value < m_begin || m_count.empty()) {
218-
_reset_bins(value);
219-
}
220-
if (value < m_begin && m_max_size_exceeded) {
221-
m_out_of_range_count += weight;
222-
return;
225+
this->_insert(std::log10(value), weight);
226+
} else {
227+
this->_insert(value, weight);
223228
}
229+
}
224230

225-
double _tol = 1e-10;
226-
int bin = std::floor((value - m_begin) / m_bin_width + _tol);
227-
228-
while (bin >= m_count.size()) {
229-
if (m_count.size() == m_max_size) {
230-
m_max_size_exceeded = true;
231-
m_out_of_range_count += weight;
232-
return;
233-
}
234-
m_count.push_back(0);
231+
/// \brief Insert the log of a value into a log space histogram directly,
232+
/// with an optional weight.
233+
///
234+
/// Notes:
235+
/// - This function is used to update the histogram when the logarithm
236+
/// (std::log10) of the "real value" has already been evaluated.
237+
/// - This function throws if the histogram is not in log space.
238+
///
239+
/// \param log_value The logarithm (std::log10) of the "real value"
240+
/// \param weight The weight to assign to the value
241+
void Histogram1D::insert_log_value(double log_value, double weight) {
242+
if (!m_is_log) {
243+
throw std::runtime_error(
244+
"Error in Histogram1D::insert_log_value: histogram is not in log "
245+
"space");
235246
}
236-
237-
m_count[bin] += weight;
247+
this->_insert(log_value, weight);
238248
}
239249

240250
/// \brief Return the coordinates of the beginning of each bin range
@@ -293,17 +303,56 @@ void Histogram1D::merge(Histogram1D const &other) {
293303
// Merge the counts
294304
std::vector<double> other_bin_coords = other.bin_coords();
295305
for (Index i = 0; i < other.m_count.size(); ++i) {
296-
this->insert(other_bin_coords[i], other.m_count[i]);
306+
if (m_is_log) {
307+
this->insert_log_value(other_bin_coords[i], other.m_count[i]);
308+
} else {
309+
this->insert(other_bin_coords[i], other.m_count[i]);
310+
}
297311
}
298312
this->m_out_of_range_count += other.m_out_of_range_count;
299313
}
300314

315+
/// \brief Insert a value into the histogram, with an optional weight
316+
///
317+
/// \param value The value to add to the histogram. If `is_log` is true,
318+
/// the value should already be in log space.
319+
/// \param weight The weight to assign to the value
320+
void Histogram1D::_insert(double value, double weight) {
321+
if (value < m_begin || m_count.empty()) {
322+
_reset_bins(value);
323+
}
324+
if (value < m_begin && m_max_size_exceeded) {
325+
m_out_of_range_count += weight;
326+
return;
327+
}
328+
329+
double _tol = 1e-10;
330+
int bin = std::floor((value - m_begin) / m_bin_width + _tol);
331+
332+
while (bin >= m_count.size()) {
333+
if (m_count.size() == m_max_size) {
334+
m_max_size_exceeded = true;
335+
m_out_of_range_count += weight;
336+
return;
337+
}
338+
m_count.push_back(0);
339+
}
340+
341+
m_count[bin] += weight;
342+
}
343+
301344
/// \brief Reset histogram bins if this is the first value being added,
302345
/// or if `value` is less than `begin`
303346
///
304347
/// \param value The value to add to the histogram. If `is_log` is true,
305348
/// the value should already be in log space.
306349
void Histogram1D::_reset_bins(double value) {
350+
if (!std::isfinite(value)) {
351+
std::stringstream msg;
352+
msg << "Error in Histogram1D::_reset_bins: value (=" << value
353+
<< ") is not finite.";
354+
throw std::runtime_error(msg.str());
355+
}
307356
if (m_count.empty()) {
308357
while (value < m_begin) {
309358
m_begin -= m_bin_width;
@@ -356,6 +405,37 @@ Histogram1D combine(std::vector<Histogram1D> const &histograms) {
356405
return combined;
357406
}
358407

408+
// -- PartitionedHistogram1D --
409+
410+
PartitionedHistogram1D::PartitionedHistogram1D(
411+
std::vector<std::string> const &_partion_names, double _initial_begin,
412+
double _bin_width, bool _is_log, Index _max_size)
413+
: m_partition_names(_partion_names),
414+
m_histograms(_partion_names.size(),
415+
Histogram1D(_initial_begin, _bin_width, _is_log, _max_size)),
416+
m_up_to_date(false) {}
417+
418+
/// \brief Make the combined histogram from the partitioned histograms
419+
void PartitionedHistogram1D::_make_combined_histogram() const {
420+
m_combined_histogram = combine(m_histograms);
421+
std::vector<double> bin_coords = m_combined_histogram->bin_coords();
422+
if (!bin_coords.empty()) {
423+
auto &hist = const_cast<std::vector<Histogram1D> &>(m_histograms);
424+
for (Index i = 0; i < hist.size(); ++i) {
425+
if (m_combined_histogram->is_log()) {
426+
hist[i].insert_log_value(bin_coords.front(), 0.0);
427+
hist[i].insert_log_value(bin_coords.back(), 0.0);
428+
} else {
429+
hist[i].insert(bin_coords.front(), 0.0);
430+
hist[i].insert(bin_coords.back(), 0.0);
431+
}
432+
}
433+
}
434+
m_up_to_date = true;
435+
}
436+
437+
// -- GenericSelectedEventFunction --
438+
359439
/// \brief Constructor - default component names
360440
GenericSelectedEventFunction::GenericSelectedEventFunction(
361441
std::string _name, std::string _description, bool _requires_event_state,

0 commit comments

Comments
 (0)