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,7 +3,7 @@
3 * Version 2.0.0 released 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 Add COMPTEL analysis scripts (#3738) 7 Add COMPTEL analysis scripts (#3738)
8 Add source counts constraints to cssens (#3668) 8 Add source counts constraints to cssens (#3668)
9 Stamp all output FITS files (#3662) 9 Stamp all output FITS files (#3662)
@@ -155,10 +155,11 @@ model parameters (#3562). @@ -155,10 +155,11 @@ model parameters (#3562).
155 155
156 ctulimit - Compute upper limit 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 support - Support classes 165 support - Support classes
doc/source/admin/release_history/2.0.rst
1 .. _2.0: 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 Overview 6 Overview
@@ -24,6 +24,7 @@ Bug fixes @@ -24,6 +24,7 @@ Bug fixes
24 Improvements 24 Improvements
25 ------------ 25 ------------
26 26
  27 +* Add restart of bisection in case of bad convergence in :ref:`ctulimit`
27 * [`3662 <https://cta-redmine.irap.omp.eu/issues/3662>`_] - 28 * [`3662 <https://cta-redmine.irap.omp.eu/issues/3662>`_] -
28 Stamp all output FITS files 29 Stamp all output FITS files
29 * [`3668 <https://cta-redmine.irap.omp.eu/issues/3668>`_] - 30 * [`3668 <https://cta-redmine.irap.omp.eu/issues/3668>`_] -
src/ctulimit/ctulimit.cpp
@@ -280,17 +280,18 @@ void ctulimit::run(void) @@ -280,17 +280,18 @@ void ctulimit::run(void)
280 // Write some parameters into logger 280 // Write some parameters into logger
281 log_value(NORMAL, "Model name", m_skymodel->name()); 281 log_value(NORMAL, "Model name", m_skymodel->name());
282 log_value(NORMAL, "Parameter name", m_model_par->name()); 282 log_value(NORMAL, "Parameter name", m_model_par->name());
  283 + log_value(NORMAL, "Parameter value", m_model_par->value());
283 log_value(NORMAL, "Confidence level", gammalib::str(m_confidence*100.0)+"%"); 284 log_value(NORMAL, "Confidence level", gammalib::str(m_confidence*100.0)+"%");
284 - log_value(NORMAL, "Best log-likelihood value", m_best_logL);  
285 log_value(NORMAL, "Log-likelihood difference", m_dlogL); 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 "["+gammalib::str(parmin)+", "+gammalib::str(parmax)+"]"); 287 "["+gammalib::str(parmin)+", "+gammalib::str(parmax)+"]");
288 288
289 // Compute upper limit 289 // Compute upper limit
290 ulimit_bisection(parmin, parmax); 290 ulimit_bisection(parmin, parmax);
291 291
292 // Write final parameter into logger 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 // Compute upper limit intensity and fluxes 296 // Compute upper limit intensity and fluxes
296 compute_ulimit(); 297 compute_ulimit();
@@ -370,6 +371,8 @@ void ctulimit::init_members(void) @@ -370,6 +371,8 @@ void ctulimit::init_members(void)
370 m_skymodel = NULL; 371 m_skymodel = NULL;
371 m_model_par = NULL; 372 m_model_par = NULL;
372 m_best_logL = 0.0; 373 m_best_logL = 0.0;
  374 + m_best_value = 0.0;
  375 + m_best_error = 0.0;
373 m_flux_ulimit = 0.0; 376 m_flux_ulimit = 0.0;
374 m_diff_ulimit = 0.0; 377 m_diff_ulimit = 0.0;
375 m_eflux_ulimit = 0.0; 378 m_eflux_ulimit = 0.0;
@@ -403,6 +406,8 @@ void ctulimit::copy_members(const ctulimit&amp; app) @@ -403,6 +406,8 @@ void ctulimit::copy_members(const ctulimit&amp; app)
403 // Copy protected members 406 // Copy protected members
404 m_dlogL = app.m_dlogL; 407 m_dlogL = app.m_dlogL;
405 m_best_logL = app.m_best_logL; 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 m_diff_ulimit = app.m_diff_ulimit; 411 m_diff_ulimit = app.m_diff_ulimit;
407 m_flux_ulimit = app.m_flux_ulimit; 412 m_flux_ulimit = app.m_flux_ulimit;
408 m_eflux_ulimit = app.m_eflux_ulimit; 413 m_eflux_ulimit = app.m_eflux_ulimit;
@@ -603,24 +608,17 @@ void ctulimit::get_parameter_brackets(double&amp; parmin, double&amp; parmax) @@ -603,24 +608,17 @@ void ctulimit::get_parameter_brackets(double&amp; parmin, double&amp; parmax)
603 log_header1(TERSE, "Find parameter brackets"); 608 log_header1(TERSE, "Find parameter brackets");
604 609
605 // Extract current value and error 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 // If parameter error is zero then take parameter value as error 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 // Compute parameter bracketing 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 if (m_model_par->has_min() && m_model_par->factor_min() > parmin) { 622 if (m_model_par->has_min() && m_model_par->factor_min() > parmin) {
625 parmin = m_model_par->factor_min(); 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,8 +691,9 @@ void ctulimit::ulimit_bisection(const double&amp; parmin, const double&amp; parmax)
693 double wrk_min = parmin; 691 double wrk_min = parmin;
694 double wrk_max = parmax; 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 // Loop until breaking condition is reached 698 // Loop until breaking condition is reached
700 while (true) { 699 while (true) {
@@ -715,29 +714,71 @@ void ctulimit::ulimit_bisection(const double&amp; parmin, const double&amp; parmax) @@ -715,29 +714,71 @@ void ctulimit::ulimit_bisection(const double&amp; parmin, const double&amp; parmax)
715 double eval_mid = logL - (m_best_logL + m_dlogL); 714 double eval_mid = logL - (m_best_logL + m_dlogL);
716 715
717 // Log information 716 // Log information
718 - log_value(EXPLICIT, "Iteration "+gammalib::str(iter), 717 + log_value(NORMAL, "Iteration "+gammalib::str(iter),
719 "["+gammalib::str(wrk_min)+", "+gammalib::str(wrk_max)+"] " 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 // Check for convergence inside tolerance 722 // Check for convergence inside tolerance
723 if (std::abs(eval_mid) < m_tol) { 723 if (std::abs(eval_mid) < m_tol) {
724 break; 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 // Check for vanishing parameter range 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 log_string(TERSE, msg); 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 // Change boundaries for further iteration 782 // Change boundaries for further iteration
742 if (eval_mid > 0.0) { 783 if (eval_mid > 0.0) {
743 wrk_max = mid; // logL too large, new range = [wrk_min, mid] 784 wrk_max = mid; // logL too large, new range = [wrk_min, mid]
src/ctulimit/ctulimit.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 * ctulimit - Upper limit calculation tool * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -98,6 +98,8 @@ protected: @@ -98,6 +98,8 @@ protected:
98 GModelSky* m_skymodel; //!< Pointer to sky model 98 GModelSky* m_skymodel; //!< Pointer to sky model
99 GModelPar* m_model_par; //!< Pointer to model parameter 99 GModelPar* m_model_par; //!< Pointer to model parameter
100 double m_best_logL; //!< Best fit log likelihood of given model 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 double m_diff_ulimit; //!< Differential upper limit value 103 double m_diff_ulimit; //!< Differential upper limit value
102 double m_flux_ulimit; //!< Flux upper limit value 104 double m_flux_ulimit; //!< Flux upper limit value
103 double m_eflux_ulimit; //!< Energy flux upper limits 105 double m_eflux_ulimit; //!< Energy flux upper limits