Commit a6e238ac2fe6708fecc4a1632900134dd240de4e

Authored by knodlseder
1 parent 86c3e7da

add model gradient computation method

include/GObservation.hpp
@@ -96,6 +96,28 @@ protected: @@ -96,6 +96,28 @@ protected:
96 void copy_members(const GObservation& obs); 96 void copy_members(const GObservation& obs);
97 void free_members(void); 97 void free_members(void);
98 98
  99 + // Model methods
  100 + virtual double model_grad(const GModel& model, const GEvent& event, int ipar) const;
  101 +
  102 + // Model gradient kernel classes
  103 + class model_func : public GFunction {
  104 + public:
  105 + model_func(const GObservation* parent,
  106 + const GModel& model,
  107 + const GEvent& event,
  108 + int ipar) :
  109 + m_parent(parent),
  110 + m_model(&model),
  111 + m_event(&event),
  112 + m_ipar(ipar) { return; }
  113 + double eval(double x);
  114 + protected:
  115 + const GObservation* m_parent; //!< Pointer to parent
  116 + const GModel* m_model; //!< Pointer to model
  117 + const GEvent* m_event; //!< Pointer to event
  118 + int m_ipar; //!< Parameter index
  119 + };
  120 +
99 // Npred methods 121 // Npred methods
100 virtual double npred_grad(const GModel& model, int ipar) const; 122 virtual double npred_grad(const GModel& model, int ipar) const;
101 virtual double npred_temp(const GModel& model) const; 123 virtual double npred_temp(const GModel& model) const;
@@ -104,10 +126,12 @@ protected: @@ -104,10 +126,12 @@ protected:
104 // Npred kernel classes 126 // Npred kernel classes
105 class npred_kern_spat : public GIntegrand { 127 class npred_kern_spat : public GIntegrand {
106 public: 128 public:
107 - npred_kern_spat(const GObservation* parent, const GModel& model,  
108 - const GTime& obsTime) :  
109 - m_parent(parent), m_model(&model), m_time(&obsTime)  
110 - { return; } 129 + npred_kern_spat(const GObservation* parent,
  130 + const GModel& model,
  131 + const GTime& obsTime) :
  132 + m_parent(parent),
  133 + m_model(&model),
  134 + m_time(&obsTime) { return; }
111 double eval(double x); 135 double eval(double x);
112 protected: 136 protected:
113 const GObservation* m_parent; //!< Pointer to parent 137 const GObservation* m_parent; //!< Pointer to parent
@@ -116,8 +140,10 @@ protected: @@ -116,8 +140,10 @@ protected:
116 }; 140 };
117 class npred_kern_spec : public GIntegrand { 141 class npred_kern_spec : public GIntegrand {
118 public: 142 public:
119 - npred_kern_spec(const GObservation* parent, const GModel& model) :  
120 - m_parent(parent), m_model(&model) { return; } 143 + npred_kern_spec(const GObservation* parent,
  144 + const GModel& model) :
  145 + m_parent(parent),
  146 + m_model(&model) { return; }
121 double eval(double x); 147 double eval(double x);
122 protected: 148 protected:
123 const GObservation* m_parent; //!< Pointer to parent 149 const GObservation* m_parent; //!< Pointer to parent
@@ -127,8 +153,12 @@ protected: @@ -127,8 +153,12 @@ protected:
127 // Npred gradient kernel classes 153 // Npred gradient kernel classes
128 class npred_func : public GFunction { 154 class npred_func : public GFunction {
129 public: 155 public:
130 - npred_func(const GObservation* parent, const GModel& model, int ipar) :  
131 - m_parent(parent), m_model(&model), m_ipar(ipar) { ; } 156 + npred_func(const GObservation* parent,
  157 + const GModel& model,
  158 + int ipar) :
  159 + m_parent(parent),
  160 + m_model(&model),
  161 + m_ipar(ipar) { return; }
132 double eval(double x); 162 double eval(double x);
133 protected: 163 protected:
134 const GObservation* m_parent; //!< Pointer to parent 164 const GObservation* m_parent; //!< Pointer to parent
src/obs/GObservation.cpp
1 /*************************************************************************** 1 /***************************************************************************
2 - * GObservation.cpp - Observation abstract base class * 2 + * GObservation.cpp - Abstract observation base class *
3 * ----------------------------------------------------------------------- * 3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2008-2011 by Jurgen Knodlseder * 4 * copyright (C) 2008-2011 by Jurgen Knodlseder *
5 * ----------------------------------------------------------------------- * 5 * ----------------------------------------------------------------------- *
@@ -118,7 +118,7 @@ GObservation::~GObservation(void) @@ -118,7 +118,7 @@ GObservation::~GObservation(void)
118 * 118 *
119 * @param[in] obs Observation. 119 * @param[in] obs Observation.
120 * 120 *
121 - * Assign one observation to another. 121 + * Assign observation.
122 ***************************************************************************/ 122 ***************************************************************************/
123 GObservation& GObservation::operator= (const GObservation& obs) 123 GObservation& GObservation::operator= (const GObservation& obs)
124 { 124 {
@@ -152,7 +152,7 @@ GObservation&amp; GObservation::operator= (const GObservation&amp; obs) @@ -152,7 +152,7 @@ GObservation&amp; GObservation::operator= (const GObservation&amp; obs)
152 * 152 *
153 * @param[in] models Model descriptor. 153 * @param[in] models Model descriptor.
154 * @param[in] event Observed event. 154 * @param[in] event Observed event.
155 - * @param[out] gradient Pointer to gradient vector (NULL=not computed). 155 + * @param[out] gradient Pointer to gradient vector (optional).
156 * 156 *
157 * @exception GException::gradient_par_mismatch 157 * @exception GException::gradient_par_mismatch
158 * Dimension of gradient vector mismatches number of parameters. 158 * Dimension of gradient vector mismatches number of parameters.
@@ -161,21 +161,30 @@ GObservation&amp; GObservation::operator= (const GObservation&amp; obs) @@ -161,21 +161,30 @@ GObservation&amp; GObservation::operator= (const GObservation&amp; obs)
161 * model gives the probability for an event to occur with a given instrument 161 * model gives the probability for an event to occur with a given instrument
162 * direction, at a given energy and at a given time. The gradient is the 162 * direction, at a given energy and at a given time. The gradient is the
163 * parameter derivative of this probability. 163 * parameter derivative of this probability.
  164 + *
  165 + * If NULL is passed for the gradient vector, then gradients will not be
  166 + * computed.
164 ***************************************************************************/ 167 ***************************************************************************/
165 double GObservation::model(const GModels& models, const GEvent& event, 168 double GObservation::model(const GModels& models, const GEvent& event,
166 GVector* gradient) const 169 GVector* gradient) const
167 { 170 {
168 - // Verify that gradients vector has the same dimension than the  
169 - // model has parameters 171 + // Verify that gradient vector and models have the same dimension
170 #if defined(G_RANGE_CHECK) 172 #if defined(G_RANGE_CHECK)
171 - if (models.npars() != gradient->size())  
172 - throw GException::gradient_par_mismatch(G_MODEL, gradient->size(),  
173 - models.npars()); 173 + if (gradient != NULL) {
  174 + if (models.npars() != gradient->size())
  175 + throw GException::gradient_par_mismatch(G_MODEL,
  176 + gradient->size(),
  177 + models.npars());
  178 + }
174 #endif 179 #endif
175 180
176 - // Initialise model value and gradient index  
177 - double model = 0.0;  
178 - int igrad = 0; 181 + // Initialise
  182 + double model = 0.0; // Reset model value
  183 + int igrad = 0; // Reset gradient counter
  184 +
  185 + // If gradient is available then reset gradient vector elements to 0
  186 + if (gradient != NULL)
  187 + (*gradient) = 0.0;
179 188
180 // Loop over models 189 // Loop over models
181 for (int i = 0; i < models.size(); ++i) { 190 for (int i = 0; i < models.size(); ++i) {
@@ -186,24 +195,18 @@ double GObservation::model(const GModels&amp; models, const GEvent&amp; event, @@ -186,24 +195,18 @@ double GObservation::model(const GModels&amp; models, const GEvent&amp; event,
186 // Compute value and add to model 195 // Compute value and add to model
187 model += models[i].eval_gradients(event, *this); 196 model += models[i].eval_gradients(event, *this);
188 197
189 - // Optionally set gradient vector 198 + // Optionally determine model gradients
190 if (gradient != NULL) { 199 if (gradient != NULL) {
191 - for (int k = 0; k < models[i].size(); ++k, ++igrad) {  
192 - double grad = models[i][k].gradient();  
193 - (*gradient)[igrad] = (isinfinite(grad)) ? 0.0 : grad;  
194 - } 200 + for (int k = 0; k < models[i].size(); ++k)
  201 + (*gradient)[igrad+k] = model_grad(models[i], event, k);
195 } 202 }
196 -  
197 - } // endif: model was applicable  
198 203
199 - // ... otherwise set gradient vector to 0  
200 - else if (gradient != NULL) {  
201 - for (int k = 0; k < models[i].size(); ++k, ++igrad) {  
202 - (*gradient)[igrad] = 0.0;  
203 - }  
204 - } 204 + } // endif: model component was valid for instrument
205 205
206 - } // endfor: looped over models 206 + // Increment parameter counter for gradients
  207 + igrad += models[i].size();
  208 +
  209 + } // endfor: Looped over models
207 210
208 // Return 211 // Return
209 return model; 212 return model;
@@ -211,17 +214,31 @@ double GObservation::model(const GModels&amp; models, const GEvent&amp; event, @@ -211,17 +214,31 @@ double GObservation::model(const GModels&amp; models, const GEvent&amp; event,
211 214
212 215
213 /***********************************************************************//** 216 /***********************************************************************//**
214 - * @brief Return total number of predicted counts for all models. 217 + * @brief Return total number (and optionally gradient) of predicted counts
  218 + * for all models
215 * 219 *
216 * @param[in] models Models. 220 * @param[in] models Models.
217 * @param[out] gradient Model parameter gradients (optional). 221 * @param[out] gradient Model parameter gradients (optional).
218 * 222 *
219 - * Returns the total number of predicted counts within the analysis region. 223 + * @exception GException::gradient_par_mismatch
  224 + * Dimension of gradient vector mismatches number of parameters.
220 * 225 *
221 - * @todo Check consistency of gradient verctor 226 + * Returns the total number of predicted counts within the analysis region.
  227 + * If NULL is passed for the gradient vector then gradients will not be
  228 + * computed.
222 ***************************************************************************/ 229 ***************************************************************************/
223 double GObservation::npred(const GModels& models, GVector* gradient) const 230 double GObservation::npred(const GModels& models, GVector* gradient) const
224 { 231 {
  232 + // Verify that gradient vector and models have the same dimension
  233 + #if defined(G_RANGE_CHECK)
  234 + if (gradient != NULL) {
  235 + if (models.npars() != gradient->size())
  236 + throw GException::gradient_par_mismatch(G_MODEL,
  237 + gradient->size(),
  238 + models.npars());
  239 + }
  240 + #endif
  241 +
225 // Initialise 242 // Initialise
226 double npred = 0.0; // Reset predicted number of counts 243 double npred = 0.0; // Reset predicted number of counts
227 int igrad = 0; // Reset gradient counter 244 int igrad = 0; // Reset gradient counter
@@ -402,6 +419,48 @@ void GObservation::free_members(void) @@ -402,6 +419,48 @@ void GObservation::free_members(void)
402 ==========================================================================*/ 419 ==========================================================================*/
403 420
404 /***********************************************************************//** 421 /***********************************************************************//**
  422 + * @brief Returns parameter gradient of model for a given event
  423 + *
  424 + * @param[in] model Model.
  425 + * @param[in] event Event.
  426 + * @param[in] ipar Parameter index for which gradient should be returned.
  427 + ***************************************************************************/
  428 +double GObservation::model_grad(const GModel& model, const GEvent& event,
  429 + int ipar) const
  430 +{
  431 + // Initialise gradient
  432 + double grad = 0.0;
  433 +
  434 + // Compute gradient only if parameter is free
  435 + if (model[ipar].isfree()) {
  436 +
  437 + // If model has a gradient then use it
  438 + if (model[ipar].hasgrad())
  439 + grad = model[ipar].gradient();
  440 +
  441 + // ... otherwise compute it numerically
  442 + else {
  443 +
  444 + // Setup derivative function
  445 + GObservation::model_func function(this, model, event, ipar);
  446 +
  447 + // Get derivative
  448 + GDerivative derivative(&function);
  449 + double x = model[ipar].value();
  450 + derivative.eps(1.0e-4);
  451 + derivative.silent(true);
  452 + grad = derivative.value(x, 0.2*std::abs(x));
  453 +
  454 + } // endelse: computed gradient numerically
  455 +
  456 + } // endif: model parameter was free
  457 +
  458 + // Return gradient
  459 + return grad;
  460 +}
  461 +
  462 +
  463 +/***********************************************************************//**
405 * @brief Returns parameter gradient of Npred 464 * @brief Returns parameter gradient of Npred
406 * 465 *
407 * @param[in] model Gamma-ray source model. 466 * @param[in] model Gamma-ray source model.
@@ -625,6 +684,48 @@ double GObservation::npred_kern_spec::eval(double x) @@ -625,6 +684,48 @@ double GObservation::npred_kern_spec::eval(double x)
625 684
626 /*========================================================================== 685 /*==========================================================================
627 = = 686 = =
  687 + = Model gradient computation =
  688 + = =
  689 + ==========================================================================*/
  690 +
  691 +/***********************************************************************//**
  692 + * @brief Model function evaluation for gradient computation
  693 + *
  694 + * @param[in] x Function value.
  695 + *
  696 + * @todo We simply remove any parameter boundaries here for the computation
  697 + * to avoid any out of boundary errors. We may have models, however,
  698 + * for which out of bound parameters lead to illegal computations, such
  699 + * a division by zero or taking the square root of negative values.
  700 + * I cannot see any elegant method to catch this at this level.
  701 + * Eventually, the higher level method should avoid going in a
  702 + * parameter domain that is not defined.
  703 + ***************************************************************************/
  704 +double GObservation::model_func::eval(double x)
  705 +{
  706 + // Circumvent const correctness
  707 + GModel* model = (GModel*)m_model;
  708 +
  709 + // Save current model parameter
  710 + GModelPar current = (*model)[m_ipar];
  711 +
  712 + // Set requested model value. Remove any boundaries to avoid limitations.
  713 + (*model)[m_ipar].value(x);
  714 + (*model)[m_ipar].remove_range();
  715 +
  716 + // Compute model value
  717 + double value = model->eval_gradients(*m_event, *m_parent);
  718 +
  719 + // Restore current model parameter
  720 + (*model)[m_ipar] = current;
  721 +
  722 + // Return value
  723 + return value;
  724 +}
  725 +
  726 +
  727 +/*==========================================================================
  728 + = =
628 = Npred gradient integration methods = 729 = Npred gradient integration methods =
629 = = 730 = =
630 ==========================================================================*/ 731 ==========================================================================*/