Commit 63cb462cbedf3fb028274518082014fc59f5c1e0

Authored by Jürgen Knödlseder
1 parent 3d26c9ce

Add accept_dec parameter to likelihood fitting tools and use best observation in SRCLIX methods

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&amp; 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,, &quot;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  
... ...