Commit 63cb462cbedf3fb028274518082014fc59f5c1e0
1 parent
3d26c9ce
Add accept_dec parameter to likelihood fitting tools and use best observation in SRCLIX methods
Showing
10 changed files
with
116 additions
and
35 deletions
doc/source/users/reference_manual/comlixfit.rst
| ... | ... | @@ -38,7 +38,14 @@ General parameters |
| 38 | 38 | Maximum number of SRCLIX iterations. |
| 39 | 39 | |
| 40 | 40 | ``(like_accuracy = 0.05) [real]`` |
| 41 | - Absolute accuracy of maximum likelihood value. | |
| 41 | + Absolute accuracy of maximum likelihood value. The SRCLIX iterations terminate | |
| 42 | + if the log-likelihood improvement is below this accuracy. | |
| 43 | + | |
| 44 | +``(accept_dec = 0.0) [real]`` | |
| 45 | + Maximum accepted log-likelihood decrease. Setting this parameter to a positive | |
| 46 | + value with allow some decrease of the log-likelihood. This may help to get the | |
| 47 | + algorithm out of a local minimum. Use this parameter with case since it may | |
| 48 | + lead to a solution that is not the maximum likelihood solution. | |
| 42 | 49 | |
| 43 | 50 | ``(fix_spat_for_ts = no) [boolean]`` |
| 44 | 51 | Fix spatial parameters for TS computation? | ... | ... |
doc/source/users/reference_manual/comlixmap.rst
| ... | ... | @@ -31,7 +31,14 @@ General parameters |
| 31 | 31 | Maximum number of SRCLIX iterations. |
| 32 | 32 | |
| 33 | 33 | ``(like_accuracy = 0.05) [real]`` |
| 34 | - Absolute accuracy of maximum likelihood value. | |
| 34 | + Absolute accuracy of maximum likelihood value. The SRCLIX iterations terminate | |
| 35 | + if the log-likelihood improvement is below this accuracy. | |
| 36 | + | |
| 37 | +``(accept_dec = 0.0) [real]`` | |
| 38 | + Maximum accepted log-likelihood decrease. Setting this parameter to a positive | |
| 39 | + value with allow some decrease of the log-likelihood. This may help to get the | |
| 40 | + algorithm out of a local minimum. Use this parameter with case since it may | |
| 41 | + lead to a solution that is not the maximum likelihood solution. | |
| 35 | 42 | |
| 36 | 43 | ``(fix_spat_for_ts = no) [boolean]`` |
| 37 | 44 | Fix spatial parameters for TS computation? | ... | ... |
doc/source/users/reference_manual/ctlike.rst
| ... | ... | @@ -128,6 +128,12 @@ General parameters |
| 128 | 128 | log likelihood value. Converserly, decreasing the value will result in less |
| 129 | 129 | iterations at the expense of a less accurate maximum likelihood value. |
| 130 | 130 | |
| 131 | +``(accept_dec = 0.0) [real]`` | |
| 132 | + Maximum accepted log-likelihood decrease. Setting this parameter to a positive | |
| 133 | + value with allow some decrease of the log-likelihood. This may help to get the | |
| 134 | + algorithm out of a local minimum. Use this parameter with case since it may | |
| 135 | + lead to a solution that is not the maximum likelihood solution. | |
| 136 | + | |
| 131 | 137 | ``(max_iter = 50) [integer]`` |
| 132 | 138 | Maximum number of fit iterations. |
| 133 | 139 | ... | ... |
modules/comscripts/comlixfit.par
| ... | ... | @@ -28,6 +28,7 @@ outmodel, f, a, results.xml,,, "Output model definition file" |
| 28 | 28 | #================== |
| 29 | 29 | max_iter, i, h, 50,1,1000, "Maximum number of SRCLIX iterations" |
| 30 | 30 | like_accuracy, r, h, 0.05,1.0e-30,1.0, "Absolute accuracy of maximum likelihood value" |
| 31 | +accept_dec, r, h, 0.0,0.0,10.0, "Maximum accepted log-likelihood decrease" | |
| 31 | 32 | fix_spat_for_ts, b, h, no,,, "Fix spatial parameters for TS computation?" |
| 32 | 33 | fix_ext_for_initial, b, h, no,,, "Fix extension parameters for initial fit iteration?" |
| 33 | 34 | bkgmethod, s, a, BGDLIXE,PHINOR|BGDLIXA|BGDLIXE,, "Method for background computation" | ... | ... |
modules/comscripts/comlixfit.py
| ... | ... | @@ -65,6 +65,7 @@ class comlixfit(ctools.cslikelihood): |
| 65 | 65 | # Query parameters |
| 66 | 66 | self['max_iter'].integer() |
| 67 | 67 | self['like_accuracy'].real() |
| 68 | + self['accept_dec'].real() | |
| 68 | 69 | self['fix_spat_for_ts'].boolean() |
| 69 | 70 | self['fix_ext_for_initial'].boolean() |
| 70 | 71 | |
| ... | ... | @@ -265,6 +266,11 @@ class comlixfit(ctools.cslikelihood): |
| 265 | 266 | self._log_header1(gammalib.NORMAL, |
| 266 | 267 | 'Iterative maximum likelihood model fitting') |
| 267 | 268 | |
| 269 | + # Initialise optimiser | |
| 270 | + self.opt().max_iter(100) | |
| 271 | + self.opt().eps(0.005) | |
| 272 | + self.opt().accept_dec(self['accept_dec'].real()) | |
| 273 | + | |
| 268 | 274 | # Loop over iterations |
| 269 | 275 | for iter in range(niter): |
| 270 | 276 | |
| ... | ... | @@ -274,6 +280,10 @@ class comlixfit(ctools.cslikelihood): |
| 274 | 280 | # Update observations |
| 275 | 281 | self._update_obs() |
| 276 | 282 | |
| 283 | + # If first iteration then initialise best observation | |
| 284 | + if iter == 0: | |
| 285 | + best_obs = self.obs().copy() | |
| 286 | + | |
| 277 | 287 | # Optionally fix or free fixed spatial extension parameters |
| 278 | 288 | if self['fix_ext_for_initial'].boolean(): |
| 279 | 289 | if iter == 0: |
| ... | ... | @@ -284,21 +294,26 @@ class comlixfit(ctools.cslikelihood): |
| 284 | 294 | # Fit model |
| 285 | 295 | self.obs().optimize(self.opt()) |
| 286 | 296 | |
| 287 | - # Compute logL difference after first iteration | |
| 297 | + # Compute logL difference | |
| 288 | 298 | if iter > 0: |
| 289 | 299 | delta = logL - self.opt().value() |
| 300 | + else: | |
| 301 | + logL = self.opt().value() | |
| 290 | 302 | |
| 291 | - # Store maximum likelihood value | |
| 292 | - logL = self.opt().value() | |
| 303 | + # If log-likelihood improved then update best observations and | |
| 304 | + # last log-likelihood value | |
| 305 | + if delta >= 0.0: | |
| 306 | + best_obs = self.obs().copy() | |
| 307 | + logL = self.opt().value() | |
| 293 | 308 | |
| 294 | 309 | # Increment iterations counter |
| 295 | 310 | self._iter += self.opt().iter() |
| 296 | 311 | |
| 297 | 312 | # Log maximum likelihood |
| 298 | 313 | if iter == 0: |
| 299 | - result = '%.5f' % (logL) | |
| 314 | + result = '%.5f' % (self.opt().value()) | |
| 300 | 315 | else: |
| 301 | - result = '%.5f (%.5f)' % (logL, delta) | |
| 316 | + result = '%.5f (%.5f)' % (self.opt().value(), delta) | |
| 302 | 317 | self._log_value(gammalib.NORMAL, 'logL after iteration %d' % (iter+1), |
| 303 | 318 | result) |
| 304 | 319 | |
| ... | ... | @@ -314,12 +329,17 @@ class comlixfit(ctools.cslikelihood): |
| 314 | 329 | self._log_header1(gammalib.EXPLICIT, |
| 315 | 330 | 'Final maximum likelihood model fitting') |
| 316 | 331 | |
| 332 | + # Use best observations for maximum likelihood fitting. This avoids | |
| 333 | + # that observations that have a worse log-likelihood than the best | |
| 334 | + # value are used for final model fitting. | |
| 335 | + self.obs(best_obs) | |
| 336 | + | |
| 317 | 337 | # Do final model fit |
| 318 | 338 | self._final_model_fit() |
| 319 | 339 | |
| 320 | 340 | # Compute logL difference and store maximum likelihood value |
| 321 | - delta = logL - self.opt().value() | |
| 322 | - logL = self.opt().value() | |
| 341 | + delta = logL - self.opt().value() | |
| 342 | + logL = self.opt().value() | |
| 323 | 343 | |
| 324 | 344 | # Log final maximum likelihood |
| 325 | 345 | result = '%.5f (%.5f)' % (logL, delta) | ... | ... |
modules/comscripts/comlixmap.par
| ... | ... | @@ -37,6 +37,7 @@ binsz, r, a, 1.0,0.01,10.0, "Image scale (in degrees/pixel)" |
| 37 | 37 | #================== |
| 38 | 38 | max_iter, i, h, 50,1,1000, "Maximum number of SRCLIX iterations" |
| 39 | 39 | like_accuracy, r, h, 0.05,1.0e-30,1.0, "Absolute accuracy of maximum likelihood value" |
| 40 | +accept_dec, r, h, 0.0,0.0,10.0, "Maximum accepted log-likelihood decrease" | |
| 40 | 41 | fix_spat, b, h, yes,,, "Fix spatial parameters for TS map computation?" |
| 41 | 42 | bkgmethod, s, a, BGDLIXE,PHINOR|BGDLIXA|BGDLIXE,, "Method for background computation" |
| 42 | 43 | nrunav, i, h, 3,,, "Number of Chi/Psi bins used for running average" | ... | ... |
modules/comscripts/comlixmap.py
| ... | ... | @@ -69,6 +69,7 @@ class comlixmap(ctools.cslikelihood): |
| 69 | 69 | # Query parameters |
| 70 | 70 | self['like_accuracy'].real() |
| 71 | 71 | self['max_iter'].integer() |
| 72 | + self['accept_dec'].real() | |
| 72 | 73 | self['fix_spat'].boolean() |
| 73 | 74 | self['coordsys'].string() |
| 74 | 75 | self['proj'].string() |
| ... | ... | @@ -210,6 +211,9 @@ class comlixmap(ctools.cslikelihood): |
| 210 | 211 | ts : float |
| 211 | 212 | TS of test source |
| 212 | 213 | """ |
| 214 | + # Get list of flux normalisation parameters | |
| 215 | + pars = ['Prefactor', 'Normalization', 'EnergyFlux', 'PhotonFlux'] | |
| 216 | + | |
| 213 | 217 | # Loop over test source names |
| 214 | 218 | for srcname in self._srcnames: |
| 215 | 219 | |
| ... | ... | @@ -245,21 +249,31 @@ class comlixmap(ctools.cslikelihood): |
| 245 | 249 | # Update observations |
| 246 | 250 | self._update_obs() |
| 247 | 251 | |
| 252 | + # If first iteration then initialise best observation | |
| 253 | + if iter == 0: | |
| 254 | + best_obs = self.obs().copy() | |
| 255 | + | |
| 248 | 256 | # Fit model |
| 249 | 257 | self.obs().optimize(self.opt()) |
| 250 | 258 | |
| 251 | - # Compute logL difference after first iteration | |
| 259 | + # Compute log-likelihood difference or initialise | |
| 260 | + # log-likelihood value | |
| 252 | 261 | if iter > 0: |
| 253 | 262 | delta = logL - self.opt().value() |
| 263 | + else: | |
| 264 | + logL = self.opt().value() | |
| 254 | 265 | |
| 255 | - # Store maximum likelihood value | |
| 256 | - logL = self.opt().value() | |
| 266 | + # If log-likelihood improved then update best observations and | |
| 267 | + # last log-likelihood value | |
| 268 | + if delta >= 0.0: | |
| 269 | + best_obs = self.obs().copy() | |
| 270 | + logL = self.opt().value() | |
| 257 | 271 | |
| 258 | 272 | # Log maximum likelihood |
| 259 | 273 | if iter == 0: |
| 260 | - result = '%.5f' % (logL) | |
| 274 | + result = '%.5f' % (self.opt().value()) | |
| 261 | 275 | else: |
| 262 | - result = '%.5f (%.5f)' % (logL, delta) | |
| 276 | + result = '%.5f (%.5f)' % (self.opt().value(), delta) | |
| 263 | 277 | self._log_value(gammalib.NORMAL, 'logL after iteration %d' % (iter+1), |
| 264 | 278 | result) |
| 265 | 279 | |
| ... | ... | @@ -268,13 +282,37 @@ class comlixmap(ctools.cslikelihood): |
| 268 | 282 | if delta < eps: |
| 269 | 283 | break |
| 270 | 284 | |
| 271 | - # Compute TS in final model fit | |
| 272 | - ts = self._final_model_fit() | |
| 285 | + # Use best observations for maximum likelihood fitting. This avoids | |
| 286 | + # that observations that have a worse log-likelihood than the best | |
| 287 | + # value are used for final model fitting. | |
| 288 | + self.obs(best_obs) | |
| 289 | + | |
| 290 | + # Compute log-likelihood and TS for best observations | |
| 291 | + value = self._final_model_fit() | |
| 273 | 292 | |
| 274 | - # Log TS | |
| 275 | - for i, srcname in enumerate(self._srcnames): | |
| 276 | - key = 'TS %s' % srcname | |
| 277 | - self._log_value(gammalib.NORMAL, key, ts[i]) | |
| 293 | + # Compute log-likelihood difference | |
| 294 | + delta = logL - value | |
| 295 | + | |
| 296 | + # Log final maximum likelihood | |
| 297 | + result = '%.5f (%.5f)' % (value, delta) | |
| 298 | + self._log_value(gammalib.NORMAL, 'logL after final iteration', | |
| 299 | + result) | |
| 300 | + | |
| 301 | + # Collect and log TS and source normalisation | |
| 302 | + ts = [] | |
| 303 | + for srcname in self._srcnames: | |
| 304 | + source = self.obs().models()[srcname] | |
| 305 | + ts_value = float(source.ts()) | |
| 306 | + key = 'TS %s' % srcname | |
| 307 | + value = '%.3f' % ts_value | |
| 308 | + ts.append(ts_value) | |
| 309 | + for par in pars: | |
| 310 | + if source.has_par(par): | |
| 311 | + value += ' (%e +/- %e %s)' % (source[par].value(), | |
| 312 | + source[par].error(), | |
| 313 | + source[par].unit()) | |
| 314 | + break | |
| 315 | + self._log_value(gammalib.NORMAL, key, value) | |
| 278 | 316 | |
| 279 | 317 | # Return TS |
| 280 | 318 | return ts |
| ... | ... | @@ -285,8 +323,8 @@ class comlixmap(ctools.cslikelihood): |
| 285 | 323 | |
| 286 | 324 | Returns |
| 287 | 325 | ------- |
| 288 | - ts : float | |
| 289 | - TS of test source | |
| 326 | + logL : float | |
| 327 | + Log-likelihood of final model fit | |
| 290 | 328 | """ |
| 291 | 329 | # Create instance of model fitting tool |
| 292 | 330 | like = ctools.ctlike(self.obs()) |
| ... | ... | @@ -295,13 +333,11 @@ class comlixmap(ctools.cslikelihood): |
| 295 | 333 | # Run ctlike |
| 296 | 334 | like.run() |
| 297 | 335 | |
| 298 | - # Recover TS for all test sources | |
| 299 | - ts = [] | |
| 300 | - for srcname in self._srcnames: | |
| 301 | - ts.append(like.obs().models()[srcname].ts()) | |
| 336 | + # Recover results | |
| 337 | + self.obs(like.obs()) | |
| 302 | 338 | |
| 303 | - # Return TS | |
| 304 | - return ts | |
| 339 | + # Return log-likelihood value | |
| 340 | + return (like.opt().value()) | |
| 305 | 341 | |
| 306 | 342 | |
| 307 | 343 | # Public methods |
| ... | ... | @@ -357,6 +393,11 @@ class comlixmap(ctools.cslikelihood): |
| 357 | 393 | # Get a copy of the initial models |
| 358 | 394 | models = self.obs().models().copy() |
| 359 | 395 | |
| 396 | + # Initialise optimiser | |
| 397 | + self.opt().max_iter(100) | |
| 398 | + self.opt().eps(0.005) | |
| 399 | + self.opt().accept_dec(self['accept_dec'].real()) | |
| 400 | + | |
| 360 | 401 | # Loop over grid positions |
| 361 | 402 | for ipix in range(self._maps[0].npix()): |
| 362 | 403 | ... | ... |
src/ctlike/ctlike.cpp
| 1 | 1 | /*************************************************************************** |
| 2 | 2 | * ctlike - Maximum likelihood fitting tool * |
| 3 | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2010-2020 by Juergen Knoedlseder * | |
| 4 | + * copyright (C) 2010-2022 by Juergen Knoedlseder * | |
| 5 | 5 | * ----------------------------------------------------------------------- * |
| 6 | 6 | * * |
| 7 | 7 | * This program is free software: you can redistribute it and/or modify * |
| ... | ... | @@ -425,7 +425,6 @@ void ctlike::init_members(void) |
| 425 | 425 | m_outmodel.clear(); |
| 426 | 426 | m_outcovmat.clear(); |
| 427 | 427 | m_max_iter = 50; |
| 428 | - m_like_accuracy = 0.005; | |
| 429 | 428 | m_refit = false; |
| 430 | 429 | m_refit_if_failed = true; |
| 431 | 430 | m_apply_edisp = false; |
| ... | ... | @@ -455,7 +454,6 @@ void ctlike::copy_members(const ctlike& app) |
| 455 | 454 | m_outmodel = app.m_outmodel; |
| 456 | 455 | m_outcovmat = app.m_outcovmat; |
| 457 | 456 | m_max_iter = app.m_max_iter; |
| 458 | - m_like_accuracy = app.m_like_accuracy; | |
| 459 | 457 | m_refit = app.m_refit; |
| 460 | 458 | m_refit_if_failed = app.m_refit_if_failed; |
| 461 | 459 | m_apply_edisp = app.m_apply_edisp; |
| ... | ... | @@ -507,10 +505,10 @@ void ctlike::get_parameters(void) |
| 507 | 505 | } // endif: no models were associated with observations |
| 508 | 506 | |
| 509 | 507 | // Set optimizer characteristics from user parameters |
| 510 | - m_max_iter = (*this)["max_iter"].integer(); | |
| 511 | - m_like_accuracy = (*this)["like_accuracy"].real(); | |
| 512 | - m_opt.eps(m_like_accuracy); | |
| 508 | + m_max_iter = (*this)["max_iter"].integer(); | |
| 513 | 509 | m_opt.max_iter(m_max_iter); |
| 510 | + m_opt.eps((*this)["like_accuracy"].real()); | |
| 511 | + m_opt.accept_dec((*this)["accept_dec"].real()); | |
| 514 | 512 | |
| 515 | 513 | // Get other parameters |
| 516 | 514 | m_refit = (*this)["refit"].boolean(); | ... | ... |
src/ctlike/ctlike.hpp
| ... | ... | @@ -76,7 +76,6 @@ protected: |
| 76 | 76 | GFilename m_outmodel; //!< Source model output XML file name |
| 77 | 77 | GFilename m_outcovmat; //!< Covariance matrix output file name |
| 78 | 78 | int m_max_iter; //!< Maximum number of iterations |
| 79 | - double m_like_accuracy; //!< Log-likelihood accuracy | |
| 80 | 79 | bool m_refit; //!< Refitting? |
| 81 | 80 | bool m_refit_if_failed; //!< Refitting in case of failure? |
| 82 | 81 | bool m_apply_edisp; //!< Apply energy dispersion? | ... | ... |
src/ctlike/ctlike.par
| ... | ... | @@ -35,6 +35,7 @@ statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, "Optimization statisti |
| 35 | 35 | refit, b, h, no,,, "Do refitting?" |
| 36 | 36 | refit_if_failed, b, h, yes,,, "Refit if fit failed?" |
| 37 | 37 | like_accuracy, r, h, 0.005,1.0e-30,1.0, "Absolute accuracy of maximum likelihood value" |
| 38 | +accept_dec, r, h, 0.0,0.0,10.0, "Maximum accepted log-likelihood decrease" | |
| 38 | 39 | max_iter, i, h, 50,1,1000, "Maximum number of fit iterations" |
| 39 | 40 | fix_spat_for_ts, b, h, no,,, "Fix spatial parameters for TS computation?" |
| 40 | 41 | ... | ... |