Commit 7031363d390befd8f024a593c9365f62760a2de6
1 parent
baf8b207
Add restart of bisection in case of bad convergence in ctulimit
Showing
5 changed files
with
82 additions
and
37 deletions
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) |
NEWS
| @@ -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& app) | @@ -403,6 +406,8 @@ void ctulimit::copy_members(const ctulimit& 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& parmin, double& parmax) | @@ -603,24 +608,17 @@ void ctulimit::get_parameter_brackets(double& parmin, double& 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& parmin, const double& parmax) | @@ -693,8 +691,9 @@ void ctulimit::ulimit_bisection(const double& parmin, const double& 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& parmin, const double& parmax) | @@ -715,29 +714,71 @@ void ctulimit::ulimit_bisection(const double& parmin, const double& 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 |