Commit 7031363d390befd8f024a593c9365f62760a2de6

Authored by Jürgen Knödlseder
1 parent baf8b207

Add restart of bisection in case of bad convergence in ctulimit

ChangeLog
... ... @@ -3,7 +3,7 @@
3 3 * Version 2.0.0 released
4 4 ========================
5 5  
6   - Re-compute best log-likelihood in ctulimit.
  6 + Add restart of bisection in case of bad convergence in ctulimit
7 7 Add COMPTEL analysis scripts (#3738)
8 8 Add source counts constraints to cssens (#3668)
9 9 Stamp all output FITS files (#3662)
... ...
... ... @@ -155,10 +155,11 @@ model parameters (#3562).
155 155  
156 156 ctulimit - Compute upper limit
157 157 ------------------------------
158   -The best log-likelihood is recomputed for a fixed best parameter value to
159   -make sure that the value is compliant with the values obtained by the bisection
160   -algorithm. The bisection iterations are stopped with a warning if the parameter
161   -interval becomes tiny.
  158 +If the bisection results in a result that is incompatible with the previously
  159 +determine best maximum log-likelihood value or if the bisection interval tends
  160 +towards zero then update the best log-likelihood value with the current result
  161 +and restart the bisection algorithm once more. This allows for dealing with
  162 +ill-conditioned fit problems.
162 163  
163 164  
164 165 support - Support classes
... ...
doc/source/admin/release_history/2.0.rst
1 1 .. _2.0:
2 2  
3   -ctools 2.0 release (24 June 2021)
  3 +ctools 2.0 release (25 June 2021)
4 4 =================================
5 5  
6 6 Overview
... ... @@ -24,6 +24,7 @@ Bug fixes
24 24 Improvements
25 25 ------------
26 26  
  27 +* Add restart of bisection in case of bad convergence in :ref:`ctulimit`
27 28 * [`3662 <https://cta-redmine.irap.omp.eu/issues/3662>`_] -
28 29 Stamp all output FITS files
29 30 * [`3668 <https://cta-redmine.irap.omp.eu/issues/3668>`_] -
... ...
src/ctulimit/ctulimit.cpp
... ... @@ -280,17 +280,18 @@ void ctulimit::run(void)
280 280 // Write some parameters into logger
281 281 log_value(NORMAL, "Model name", m_skymodel->name());
282 282 log_value(NORMAL, "Parameter name", m_model_par->name());
  283 + log_value(NORMAL, "Parameter value", m_model_par->value());
283 284 log_value(NORMAL, "Confidence level", gammalib::str(m_confidence*100.0)+"%");
284   - log_value(NORMAL, "Best log-likelihood value", m_best_logL);
285 285 log_value(NORMAL, "Log-likelihood difference", m_dlogL);
286   - log_value(NORMAL, "Initial parameter range",
  286 + log_value(NORMAL, "Initial factor value range",
287 287 "["+gammalib::str(parmin)+", "+gammalib::str(parmax)+"]");
288 288  
289 289 // Compute upper limit
290 290 ulimit_bisection(parmin, parmax);
291 291  
292 292 // Write final parameter into logger
293   - log_value(NORMAL, "Final parameter", m_model_par->value());
  293 + log_value(NORMAL, "Upper limit factor value", m_model_par->factor_value());
  294 + log_value(NORMAL, "Upper limit param. value", m_model_par->value());
294 295  
295 296 // Compute upper limit intensity and fluxes
296 297 compute_ulimit();
... ... @@ -370,6 +371,8 @@ void ctulimit::init_members(void)
370 371 m_skymodel = NULL;
371 372 m_model_par = NULL;
372 373 m_best_logL = 0.0;
  374 + m_best_value = 0.0;
  375 + m_best_error = 0.0;
373 376 m_flux_ulimit = 0.0;
374 377 m_diff_ulimit = 0.0;
375 378 m_eflux_ulimit = 0.0;
... ... @@ -403,6 +406,8 @@ void ctulimit::copy_members(const ctulimit&amp; app)
403 406 // Copy protected members
404 407 m_dlogL = app.m_dlogL;
405 408 m_best_logL = app.m_best_logL;
  409 + m_best_value = app.m_best_value;
  410 + m_best_error = app.m_best_error;
406 411 m_diff_ulimit = app.m_diff_ulimit;
407 412 m_flux_ulimit = app.m_flux_ulimit;
408 413 m_eflux_ulimit = app.m_eflux_ulimit;
... ... @@ -603,24 +608,17 @@ void ctulimit::get_parameter_brackets(double&amp; parmin, double&amp; parmax)
603 608 log_header1(TERSE, "Find parameter brackets");
604 609  
605 610 // Extract current value and error
606   - double value = m_model_par->factor_value();
607   - double error = m_model_par->factor_error();
608   -
609   - // Re-compute best log-likelihood for value fixed at optimum
610   - m_best_logL = evaluate(*m_model_par, value);
611   -
612   - // Write best log-likelihood
613   - log_value(NORMAL, "Best log-likelihood",
614   - "logL("+gammalib::str(value)+")="+gammalib::str(m_best_logL));
  611 + m_best_value = m_model_par->factor_value();
  612 + m_best_error = m_model_par->factor_error();
615 613  
616 614 // If parameter error is zero then take parameter value as error
617   - if (error == 0.0) {
618   - error = value;
  615 + if (m_best_error == 0.0) {
  616 + m_best_error = m_best_value;
619 617 }
620 618  
621 619 // Compute parameter bracketing
622   - parmin = value - m_sigma_min * error;
623   - parmax = value + m_sigma_max * error;
  620 + parmin = m_best_value - m_sigma_min * m_best_error;
  621 + parmax = m_best_value + m_sigma_max * m_best_error;
624 622 if (m_model_par->has_min() && m_model_par->factor_min() > parmin) {
625 623 parmin = m_model_par->factor_min();
626 624 }
... ... @@ -693,8 +691,9 @@ void ctulimit::ulimit_bisection(const double&amp; parmin, const double&amp; parmax)
693 691 double wrk_min = parmin;
694 692 double wrk_max = parmax;
695 693  
696   - // Initialise iteration counter
697   - int iter = 0;
  694 + // Initialise iteration counter and restart flag
  695 + int iter = 0;
  696 + bool restart = false;
698 697  
699 698 // Loop until breaking condition is reached
700 699 while (true) {
... ... @@ -715,29 +714,71 @@ void ctulimit::ulimit_bisection(const double&amp; parmin, const double&amp; parmax)
715 714 double eval_mid = logL - (m_best_logL + m_dlogL);
716 715  
717 716 // Log information
718   - log_value(EXPLICIT, "Iteration "+gammalib::str(iter),
  717 + log_value(NORMAL, "Iteration "+gammalib::str(iter),
719 718 "["+gammalib::str(wrk_min)+", "+gammalib::str(wrk_max)+"] "
720   - "dlogL("+gammalib::str(mid)+")="+gammalib::str(eval_mid));
  719 + "logL("+gammalib::str(mid)+")="+gammalib::str(logL)+" "
  720 + "dlogL="+gammalib::str(eval_mid));
721 721  
722 722 // Check for convergence inside tolerance
723 723 if (std::abs(eval_mid) < m_tol) {
724 724 break;
725 725 }
726 726  
  727 + // Assess status
  728 + int status = 0;
  729 +
  730 + // Check whether mid-point is close to best parameter value
  731 + double eps = (mid != 0.0) ? (mid - m_best_value)/mid : mid - m_best_value;
  732 + if (std::abs(eps) < 1.0e-6) {
  733 + std::string msg =
  734 + " *** WARNING: Parameter range mid-point "+gammalib::str(mid)+" is "
  735 + "close to best factor\n"
  736 + " value "+gammalib::str(m_best_value)+" yet the "
  737 + "log-likelihood value "+gammalib::str(logL)+"\n"
  738 + " at the mid-point differs significantly from the "
  739 + "log-likelihood value\n"
  740 + " "+gammalib::str(m_best_logL)+" for the best factor "
  741 + "value.";
  742 + log_string(TERSE, msg);
  743 + status = 1;
  744 + }
  745 +
727 746 // Check for vanishing parameter range
728   - if (std::abs(wrk_min-wrk_max) < 1.0e-6) {
729   - std::string msg = "*** WARNING: Parameter range "
730   - "["+gammalib::str(wrk_min)+", "+
731   - gammalib::str(wrk_max)+"] has reduced to a "
732   - "narrow interval without reaching convergence!\n"
733   - " The best log-likelihood value is "
734   - "probably not an absolute but only a local "
735   - "minimum.\n"
736   - " Stop iterations.";
  747 + eps = (mid != 0.0) ? (wrk_min - wrk_max)/mid : wrk_min - wrk_max;
  748 + if (std::abs(eps) < 1.0e-6) {
  749 + std::string msg =
  750 + " *** WARNING: Parameter range ["+gammalib::str(wrk_min)+", "+
  751 + gammalib::str(wrk_max)+"] has reduced\n"
  752 + " to a narrow interval without reaching convergence! "
  753 + "The best\n"
  754 + " log-likelihood value is probably not an absolute "
  755 + "but only a local\n"
  756 + " minimum.";
737 757 log_string(TERSE, msg);
738   - break;
  758 + status = 2;
739 759 }
740 760  
  761 + // Restart bisection if a restart is required
  762 + if (!restart && status != 0) {
  763 +
  764 + // Adopt current logL as best log-likelihood
  765 + restart = true;
  766 + iter = 0;
  767 + wrk_min = parmin;
  768 + wrk_max = parmax;
  769 + m_best_logL = logL;
  770 +
  771 + // Signal restart
  772 + std::string msg =
  773 + " Adopt "+gammalib::str(logL)+" as best log-likelihood and restart "
  774 + "bisection";
  775 + log_string(TERSE, msg);
  776 +
  777 + // Restart
  778 + continue;
  779 +
  780 + } // endif: restart bisection if required
  781 +
741 782 // Change boundaries for further iteration
742 783 if (eval_mid > 0.0) {
743 784 wrk_max = mid; // logL too large, new range = [wrk_min, mid]
... ...
src/ctulimit/ctulimit.hpp
1 1 /***************************************************************************
2 2 * ctulimit - Upper limit calculation tool *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2015-2018 by Michael Mayer *
  4 + * copyright (C) 2015-2021 by Michael Mayer *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -98,6 +98,8 @@ protected:
98 98 GModelSky* m_skymodel; //!< Pointer to sky model
99 99 GModelPar* m_model_par; //!< Pointer to model parameter
100 100 double m_best_logL; //!< Best fit log likelihood of given model
  101 + double m_best_value; //!< Best parameter value factor
  102 + double m_best_error; //!< Best parameter value error
101 103 double m_diff_ulimit; //!< Differential upper limit value
102 104 double m_flux_ulimit; //!< Flux upper limit value
103 105 double m_eflux_ulimit; //!< Energy flux upper limits
... ...