Commit 784e9fe5cf17dfe651c77c5c748c5041a4be22ca
1 parent
b45c4d28
add remove flag to fix parameters with zero covariance
Showing
2 changed files
with
42 additions
and
22 deletions
include/GOptimizerLM.hpp
| ... | ... | @@ -114,9 +114,10 @@ protected: |
| 114 | 114 | std::vector<int> m_hit_minimum; //!< Bookkeeping of successive minimum hits |
| 115 | 115 | std::vector<int> m_hit_maximum; //!< Bookkeeping of successive maximum hits |
| 116 | 116 | std::vector<bool> m_par_freeze; //!< Bookkeeping of parameter freeze |
| 117 | + std::vector<bool> m_par_remove; //!< Bookkeeping of parameter removal | |
| 117 | 118 | double m_lambda; //!< Actual lambda |
| 118 | 119 | double m_value; //!< Actual function value |
| 119 | - double m_diag_load; //!< Value for diagonal loading | |
| 120 | + //double m_diag_load; //!< Value for diagonal loading | |
| 120 | 121 | int m_status; //!< Fit status |
| 121 | 122 | int m_iter; //!< Iteration |
| 122 | 123 | GLog* m_logger; //!< Pointer to optional logger | ... | ... |
src/opt/GOptimizerLM.cpp
| ... | ... | @@ -289,13 +289,14 @@ void GOptimizerLM::init_members(void) |
| 289 | 289 | m_max_stall = 10; |
| 290 | 290 | m_max_hit = 3; //!< Maximum successive boundary hits before freeze |
| 291 | 291 | m_step_adjust = true; |
| 292 | - m_diag_load = 1.0e-100; | |
| 292 | + //m_diag_load = 1.0e-100; | |
| 293 | 293 | |
| 294 | 294 | // Initialise bookkeeping arrays |
| 295 | 295 | m_hit_boundary.clear(); |
| 296 | 296 | m_hit_minimum.clear(); |
| 297 | 297 | m_hit_maximum.clear(); |
| 298 | 298 | m_par_freeze.clear(); |
| 299 | + m_par_remove.clear(); | |
| 299 | 300 | |
| 300 | 301 | // Initialise optimizer values |
| 301 | 302 | m_lambda = m_lambda_start; |
| ... | ... | @@ -326,11 +327,12 @@ void GOptimizerLM::copy_members(const GOptimizerLM& opt) |
| 326 | 327 | m_max_iter = opt.m_max_iter; |
| 327 | 328 | m_max_stall = opt.m_max_stall; |
| 328 | 329 | m_step_adjust = opt.m_step_adjust; |
| 329 | - m_diag_load = opt.m_diag_load; | |
| 330 | + //m_diag_load = opt.m_diag_load; | |
| 330 | 331 | m_hit_boundary = opt.m_hit_boundary; |
| 331 | 332 | m_hit_minimum = opt.m_hit_minimum; |
| 332 | 333 | m_hit_maximum = opt.m_hit_maximum; |
| 333 | 334 | m_par_freeze = opt.m_par_freeze; |
| 335 | + m_par_remove = opt.m_par_remove; | |
| 334 | 336 | m_lambda = opt.m_lambda; |
| 335 | 337 | m_value = opt.m_value; |
| 336 | 338 | m_status = opt.m_status; |
| ... | ... | @@ -383,33 +385,43 @@ void GOptimizerLM::optimize(GOptimizerFunction* fct, GOptimizerPars* pars) |
| 383 | 385 | m_hit_minimum.clear(); |
| 384 | 386 | m_hit_maximum.clear(); |
| 385 | 387 | m_par_freeze.clear(); |
| 388 | + m_par_remove.clear(); | |
| 386 | 389 | m_hit_boundary.reserve(m_npars); |
| 387 | 390 | m_hit_minimum.reserve(m_npars); |
| 388 | 391 | m_hit_maximum.reserve(m_npars); |
| 389 | 392 | m_par_freeze.reserve(m_npars); |
| 393 | + m_par_remove.reserve(m_npars); | |
| 390 | 394 | for (int i = 0; i < m_npars; ++i) { |
| 391 | 395 | m_hit_boundary.push_back(false); |
| 392 | 396 | m_hit_minimum.push_back(0); |
| 393 | 397 | m_hit_maximum.push_back(0); |
| 394 | 398 | m_par_freeze.push_back(false); |
| 399 | + m_par_remove.push_back(false); | |
| 395 | 400 | } |
| 396 | 401 | |
| 397 | 402 | // Initial evaluation |
| 398 | 403 | fct->eval(*pars); |
| 399 | 404 | |
| 400 | 405 | // If a free parameter has a zero diagonal element in the curvature |
| 401 | - // matrix then load it with a small value so that it will be fitted. | |
| 402 | - // This is needed in the LM algorithm as we don't use the full Hessian | |
| 403 | - // matrix but only the curvature. The problem appears in the unbinned | |
| 406 | + // matrix then remove this parameter definitely from the fit as it | |
| 407 | + // otherwise will block the fit. The problem appears in the unbinned | |
| 404 | 408 | // fitting where parameter gradients may be zero (due to the truncation |
| 405 | - // of the PSF), but the Npred gradient is not zero. As the Npred | |
| 406 | - // curvature is by definition zero, we would never move the | |
| 407 | - // corresponding fit parameter, and thus block the fit. This is | |
| 408 | - // prevented by this kluge. | |
| 409 | + // of the PSF), but the Npred gradient is not zero. In principle we | |
| 410 | + // could use the Npred gradient for fitting (I guess), but I still | |
| 411 | + // have to figure out how ... (the diagonal loading was not so | |
| 412 | + // successful as it faked early convergence) | |
| 409 | 413 | for (int ipar = 0; ipar < m_npars; ++ipar) { |
| 410 | 414 | if (pars->par(ipar).isfree()) { |
| 411 | - if ((*fct->covar())(ipar,ipar) == 0.0) | |
| 412 | - (*fct->covar())(ipar,ipar) = m_diag_load; | |
| 415 | + if ((*fct->covar())(ipar,ipar) == 0.0) { | |
| 416 | + //(*fct->covar())(ipar,ipar) = m_diag_load; | |
| 417 | + if (m_logger != NULL) { | |
| 418 | + *m_logger << " Parameter \"" << pars->par(ipar).name(); | |
| 419 | + *m_logger << "\" has zero covariance."; | |
| 420 | + *m_logger << " Fix parameter." << std::endl; | |
| 421 | + } | |
| 422 | + m_par_remove[ipar] = true; | |
| 423 | + pars->par(ipar).fix(); | |
| 424 | + } | |
| 413 | 425 | } |
| 414 | 426 | } |
| 415 | 427 | |
| ... | ... | @@ -560,7 +572,7 @@ void GOptimizerLM::optimize(GOptimizerFunction* fct, GOptimizerPars* pars) |
| 560 | 572 | // Free now all temporarily frozen parameters so that the resulting |
| 561 | 573 | // model has the same attributes as the initial model |
| 562 | 574 | for (int ipar = 0; ipar < m_npars; ++ipar) { |
| 563 | - if (m_par_freeze[ipar]) { | |
| 575 | + if (m_par_freeze[ipar] || m_par_remove[ipar]) { | |
| 564 | 576 | pars->par(ipar).free(); |
| 565 | 577 | if (m_logger != NULL) { |
| 566 | 578 | *m_logger << " Free parameter \"" |
| ... | ... | @@ -748,18 +760,25 @@ void GOptimizerLM::iteration(GOptimizerFunction* fct, GOptimizerPars* pars) |
| 748 | 760 | fct->eval(*pars); |
| 749 | 761 | |
| 750 | 762 | // If a free parameter has a zero diagonal element in the curvature |
| 751 | - // matrix then load it with a small value so that it will be fitted. | |
| 752 | - // This is needed in the LM algorithm as we don't use the full Hessian | |
| 753 | - // matrix but only the curvature. The problem appears in the unbinned | |
| 763 | + // matrix then remove this parameter definitely from the fit as it | |
| 764 | + // otherwise will block the fit. The problem appears in the unbinned | |
| 754 | 765 | // fitting where parameter gradients may be zero (due to the truncation |
| 755 | - // of the PSF), but the Npred gradient is not zero. As the Npred | |
| 756 | - // curvature is by definition zero, we would never move the | |
| 757 | - // corresponding fit parameter, and thus block the fit. This is | |
| 758 | - // prevented by this kluge. | |
| 766 | + // of the PSF), but the Npred gradient is not zero. In principle we | |
| 767 | + // could use the Npred gradient for fitting (I guess), but I still | |
| 768 | + // have to figure out how ... (the diagonal loading was not so | |
| 769 | + // successful as it faked early convergence) | |
| 759 | 770 | for (int ipar = 0; ipar < m_npars; ++ipar) { |
| 760 | 771 | if (pars->par(ipar).isfree()) { |
| 761 | - if ((*fct->covar())(ipar,ipar) == 0.0) | |
| 762 | - (*fct->covar())(ipar,ipar) = m_diag_load; | |
| 772 | + if ((*fct->covar())(ipar,ipar) == 0.0) { | |
| 773 | + //(*fct->covar())(ipar,ipar) = m_diag_load; | |
| 774 | + if (m_logger != NULL) { | |
| 775 | + *m_logger << " Parameter \"" << pars->par(ipar).name(); | |
| 776 | + *m_logger << "\" has zero covariance."; | |
| 777 | + *m_logger << " Fix parameter." << std::endl; | |
| 778 | + } | |
| 779 | + m_par_remove[ipar] = true; | |
| 780 | + pars->par(ipar).fix(); | |
| 781 | + } | |
| 763 | 782 | } |
| 764 | 783 | } |
| 765 | 784 | ... | ... |