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,7 +38,14 @@ General parameters
38 Maximum number of SRCLIX iterations. 38 Maximum number of SRCLIX iterations.
39 39
40 ``(like_accuracy = 0.05) [real]`` 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 ``(fix_spat_for_ts = no) [boolean]`` 50 ``(fix_spat_for_ts = no) [boolean]``
44 Fix spatial parameters for TS computation? 51 Fix spatial parameters for TS computation?
doc/source/users/reference_manual/comlixmap.rst
@@ -31,7 +31,14 @@ General parameters @@ -31,7 +31,14 @@ General parameters
31 Maximum number of SRCLIX iterations. 31 Maximum number of SRCLIX iterations.
32 32
33 ``(like_accuracy = 0.05) [real]`` 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 ``(fix_spat_for_ts = no) [boolean]`` 43 ``(fix_spat_for_ts = no) [boolean]``
37 Fix spatial parameters for TS computation? 44 Fix spatial parameters for TS computation?
doc/source/users/reference_manual/ctlike.rst
@@ -128,6 +128,12 @@ General parameters @@ -128,6 +128,12 @@ General parameters
128 log likelihood value. Converserly, decreasing the value will result in less 128 log likelihood value. Converserly, decreasing the value will result in less
129 iterations at the expense of a less accurate maximum likelihood value. 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 ``(max_iter = 50) [integer]`` 137 ``(max_iter = 50) [integer]``
132 Maximum number of fit iterations. 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,6 +28,7 @@ outmodel, f, a, results.xml,,, "Output model definition file"
28 #================== 28 #==================
29 max_iter, i, h, 50,1,1000, "Maximum number of SRCLIX iterations" 29 max_iter, i, h, 50,1,1000, "Maximum number of SRCLIX iterations"
30 like_accuracy, r, h, 0.05,1.0e-30,1.0, "Absolute accuracy of maximum likelihood value" 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 fix_spat_for_ts, b, h, no,,, "Fix spatial parameters for TS computation?" 32 fix_spat_for_ts, b, h, no,,, "Fix spatial parameters for TS computation?"
32 fix_ext_for_initial, b, h, no,,, "Fix extension parameters for initial fit iteration?" 33 fix_ext_for_initial, b, h, no,,, "Fix extension parameters for initial fit iteration?"
33 bkgmethod, s, a, BGDLIXE,PHINOR|BGDLIXA|BGDLIXE,, "Method for background computation" 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,6 +65,7 @@ class comlixfit(ctools.cslikelihood):
65 # Query parameters 65 # Query parameters
66 self['max_iter'].integer() 66 self['max_iter'].integer()
67 self['like_accuracy'].real() 67 self['like_accuracy'].real()
  68 + self['accept_dec'].real()
68 self['fix_spat_for_ts'].boolean() 69 self['fix_spat_for_ts'].boolean()
69 self['fix_ext_for_initial'].boolean() 70 self['fix_ext_for_initial'].boolean()
70 71
@@ -265,6 +266,11 @@ class comlixfit(ctools.cslikelihood): @@ -265,6 +266,11 @@ class comlixfit(ctools.cslikelihood):
265 self._log_header1(gammalib.NORMAL, 266 self._log_header1(gammalib.NORMAL,
266 'Iterative maximum likelihood model fitting') 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 # Loop over iterations 274 # Loop over iterations
269 for iter in range(niter): 275 for iter in range(niter):
270 276
@@ -274,6 +280,10 @@ class comlixfit(ctools.cslikelihood): @@ -274,6 +280,10 @@ class comlixfit(ctools.cslikelihood):
274 # Update observations 280 # Update observations
275 self._update_obs() 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 # Optionally fix or free fixed spatial extension parameters 287 # Optionally fix or free fixed spatial extension parameters
278 if self['fix_ext_for_initial'].boolean(): 288 if self['fix_ext_for_initial'].boolean():
279 if iter == 0: 289 if iter == 0:
@@ -284,21 +294,26 @@ class comlixfit(ctools.cslikelihood): @@ -284,21 +294,26 @@ class comlixfit(ctools.cslikelihood):
284 # Fit model 294 # Fit model
285 self.obs().optimize(self.opt()) 295 self.obs().optimize(self.opt())
286 296
287 - # Compute logL difference after first iteration 297 + # Compute logL difference
288 if iter > 0: 298 if iter > 0:
289 delta = logL - self.opt().value() 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 # Increment iterations counter 309 # Increment iterations counter
295 self._iter += self.opt().iter() 310 self._iter += self.opt().iter()
296 311
297 # Log maximum likelihood 312 # Log maximum likelihood
298 if iter == 0: 313 if iter == 0:
299 - result = '%.5f' % (logL) 314 + result = '%.5f' % (self.opt().value())
300 else: 315 else:
301 - result = '%.5f (%.5f)' % (logL, delta) 316 + result = '%.5f (%.5f)' % (self.opt().value(), delta)
302 self._log_value(gammalib.NORMAL, 'logL after iteration %d' % (iter+1), 317 self._log_value(gammalib.NORMAL, 'logL after iteration %d' % (iter+1),
303 result) 318 result)
304 319
@@ -314,12 +329,17 @@ class comlixfit(ctools.cslikelihood): @@ -314,12 +329,17 @@ class comlixfit(ctools.cslikelihood):
314 self._log_header1(gammalib.EXPLICIT, 329 self._log_header1(gammalib.EXPLICIT,
315 'Final maximum likelihood model fitting') 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 # Do final model fit 337 # Do final model fit
318 self._final_model_fit() 338 self._final_model_fit()
319 339
320 # Compute logL difference and store maximum likelihood value 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 # Log final maximum likelihood 344 # Log final maximum likelihood
325 result = '%.5f (%.5f)' % (logL, delta) 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,6 +37,7 @@ binsz, r, a, 1.0,0.01,10.0, "Image scale (in degrees/pixel)"
37 #================== 37 #==================
38 max_iter, i, h, 50,1,1000, "Maximum number of SRCLIX iterations" 38 max_iter, i, h, 50,1,1000, "Maximum number of SRCLIX iterations"
39 like_accuracy, r, h, 0.05,1.0e-30,1.0, "Absolute accuracy of maximum likelihood value" 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 fix_spat, b, h, yes,,, "Fix spatial parameters for TS map computation?" 41 fix_spat, b, h, yes,,, "Fix spatial parameters for TS map computation?"
41 bkgmethod, s, a, BGDLIXE,PHINOR|BGDLIXA|BGDLIXE,, "Method for background computation" 42 bkgmethod, s, a, BGDLIXE,PHINOR|BGDLIXA|BGDLIXE,, "Method for background computation"
42 nrunav, i, h, 3,,, "Number of Chi/Psi bins used for running average" 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,6 +69,7 @@ class comlixmap(ctools.cslikelihood):
69 # Query parameters 69 # Query parameters
70 self['like_accuracy'].real() 70 self['like_accuracy'].real()
71 self['max_iter'].integer() 71 self['max_iter'].integer()
  72 + self['accept_dec'].real()
72 self['fix_spat'].boolean() 73 self['fix_spat'].boolean()
73 self['coordsys'].string() 74 self['coordsys'].string()
74 self['proj'].string() 75 self['proj'].string()
@@ -210,6 +211,9 @@ class comlixmap(ctools.cslikelihood): @@ -210,6 +211,9 @@ class comlixmap(ctools.cslikelihood):
210 ts : float 211 ts : float
211 TS of test source 212 TS of test source
212 """ 213 """
  214 + # Get list of flux normalisation parameters
  215 + pars = ['Prefactor', 'Normalization', 'EnergyFlux', 'PhotonFlux']
  216 +
213 # Loop over test source names 217 # Loop over test source names
214 for srcname in self._srcnames: 218 for srcname in self._srcnames:
215 219
@@ -245,21 +249,31 @@ class comlixmap(ctools.cslikelihood): @@ -245,21 +249,31 @@ class comlixmap(ctools.cslikelihood):
245 # Update observations 249 # Update observations
246 self._update_obs() 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 # Fit model 256 # Fit model
249 self.obs().optimize(self.opt()) 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 if iter > 0: 261 if iter > 0:
253 delta = logL - self.opt().value() 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 # Log maximum likelihood 272 # Log maximum likelihood
259 if iter == 0: 273 if iter == 0:
260 - result = '%.5f' % (logL) 274 + result = '%.5f' % (self.opt().value())
261 else: 275 else:
262 - result = '%.5f (%.5f)' % (logL, delta) 276 + result = '%.5f (%.5f)' % (self.opt().value(), delta)
263 self._log_value(gammalib.NORMAL, 'logL after iteration %d' % (iter+1), 277 self._log_value(gammalib.NORMAL, 'logL after iteration %d' % (iter+1),
264 result) 278 result)
265 279
@@ -268,13 +282,37 @@ class comlixmap(ctools.cslikelihood): @@ -268,13 +282,37 @@ class comlixmap(ctools.cslikelihood):
268 if delta < eps: 282 if delta < eps:
269 break 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 # Return TS 317 # Return TS
280 return ts 318 return ts
@@ -285,8 +323,8 @@ class comlixmap(ctools.cslikelihood): @@ -285,8 +323,8 @@ class comlixmap(ctools.cslikelihood):
285 323
286 Returns 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 # Create instance of model fitting tool 329 # Create instance of model fitting tool
292 like = ctools.ctlike(self.obs()) 330 like = ctools.ctlike(self.obs())
@@ -295,13 +333,11 @@ class comlixmap(ctools.cslikelihood): @@ -295,13 +333,11 @@ class comlixmap(ctools.cslikelihood):
295 # Run ctlike 333 # Run ctlike
296 like.run() 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 # Public methods 343 # Public methods
@@ -357,6 +393,11 @@ class comlixmap(ctools.cslikelihood): @@ -357,6 +393,11 @@ class comlixmap(ctools.cslikelihood):
357 # Get a copy of the initial models 393 # Get a copy of the initial models
358 models = self.obs().models().copy() 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 # Loop over grid positions 401 # Loop over grid positions
361 for ipix in range(self._maps[0].npix()): 402 for ipix in range(self._maps[0].npix()):
362 403
src/ctlike/ctlike.cpp
1 /*************************************************************************** 1 /***************************************************************************
2 * ctlike - Maximum likelihood fitting tool * 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 * 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 *
@@ -425,7 +425,6 @@ void ctlike::init_members(void) @@ -425,7 +425,6 @@ void ctlike::init_members(void)
425 m_outmodel.clear(); 425 m_outmodel.clear();
426 m_outcovmat.clear(); 426 m_outcovmat.clear();
427 m_max_iter = 50; 427 m_max_iter = 50;
428 - m_like_accuracy = 0.005;  
429 m_refit = false; 428 m_refit = false;
430 m_refit_if_failed = true; 429 m_refit_if_failed = true;
431 m_apply_edisp = false; 430 m_apply_edisp = false;
@@ -455,7 +454,6 @@ void ctlike::copy_members(const ctlike&amp; app) @@ -455,7 +454,6 @@ void ctlike::copy_members(const ctlike&amp; app)
455 m_outmodel = app.m_outmodel; 454 m_outmodel = app.m_outmodel;
456 m_outcovmat = app.m_outcovmat; 455 m_outcovmat = app.m_outcovmat;
457 m_max_iter = app.m_max_iter; 456 m_max_iter = app.m_max_iter;
458 - m_like_accuracy = app.m_like_accuracy;  
459 m_refit = app.m_refit; 457 m_refit = app.m_refit;
460 m_refit_if_failed = app.m_refit_if_failed; 458 m_refit_if_failed = app.m_refit_if_failed;
461 m_apply_edisp = app.m_apply_edisp; 459 m_apply_edisp = app.m_apply_edisp;
@@ -507,10 +505,10 @@ void ctlike::get_parameters(void) @@ -507,10 +505,10 @@ void ctlike::get_parameters(void)
507 } // endif: no models were associated with observations 505 } // endif: no models were associated with observations
508 506
509 // Set optimizer characteristics from user parameters 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 m_opt.max_iter(m_max_iter); 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 // Get other parameters 513 // Get other parameters
516 m_refit = (*this)["refit"].boolean(); 514 m_refit = (*this)["refit"].boolean();
src/ctlike/ctlike.hpp
@@ -76,7 +76,6 @@ protected: @@ -76,7 +76,6 @@ protected:
76 GFilename m_outmodel; //!< Source model output XML file name 76 GFilename m_outmodel; //!< Source model output XML file name
77 GFilename m_outcovmat; //!< Covariance matrix output file name 77 GFilename m_outcovmat; //!< Covariance matrix output file name
78 int m_max_iter; //!< Maximum number of iterations 78 int m_max_iter; //!< Maximum number of iterations
79 - double m_like_accuracy; //!< Log-likelihood accuracy  
80 bool m_refit; //!< Refitting? 79 bool m_refit; //!< Refitting?
81 bool m_refit_if_failed; //!< Refitting in case of failure? 80 bool m_refit_if_failed; //!< Refitting in case of failure?
82 bool m_apply_edisp; //!< Apply energy dispersion? 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,6 +35,7 @@ statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, &quot;Optimization statisti
35 refit, b, h, no,,, "Do refitting?" 35 refit, b, h, no,,, "Do refitting?"
36 refit_if_failed, b, h, yes,,, "Refit if fit failed?" 36 refit_if_failed, b, h, yes,,, "Refit if fit failed?"
37 like_accuracy, r, h, 0.005,1.0e-30,1.0, "Absolute accuracy of maximum likelihood value" 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 max_iter, i, h, 50,1,1000, "Maximum number of fit iterations" 39 max_iter, i, h, 50,1,1000, "Maximum number of fit iterations"
39 fix_spat_for_ts, b, h, no,,, "Fix spatial parameters for TS computation?" 40 fix_spat_for_ts, b, h, no,,, "Fix spatial parameters for TS computation?"
40 41