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 | 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) | ... | ... |
NEWS
| ... | ... | @@ -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& 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& parmin, double& 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& parmin, const double& 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& parmin, const double& 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 | ... | ... |