Commit a6e238ac2fe6708fecc4a1632900134dd240de4e

Authored by knodlseder
1 parent 86c3e7da

add model gradient computation method

include/GObservation.hpp
... ... @@ -96,6 +96,28 @@ protected:
96 96 void copy_members(const GObservation& obs);
97 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 121 // Npred methods
100 122 virtual double npred_grad(const GModel& model, int ipar) const;
101 123 virtual double npred_temp(const GModel& model) const;
... ... @@ -104,10 +126,12 @@ protected:
104 126 // Npred kernel classes
105 127 class npred_kern_spat : public GIntegrand {
106 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 135 double eval(double x);
112 136 protected:
113 137 const GObservation* m_parent; //!< Pointer to parent
... ... @@ -116,8 +140,10 @@ protected:
116 140 };
117 141 class npred_kern_spec : public GIntegrand {
118 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 147 double eval(double x);
122 148 protected:
123 149 const GObservation* m_parent; //!< Pointer to parent
... ... @@ -127,8 +153,12 @@ protected:
127 153 // Npred gradient kernel classes
128 154 class npred_func : public GFunction {
129 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 162 double eval(double x);
133 163 protected:
134 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 4 * copyright (C) 2008-2011 by Jurgen Knodlseder *
5 5 * ----------------------------------------------------------------------- *
... ... @@ -118,7 +118,7 @@ GObservation::~GObservation(void)
118 118 *
119 119 * @param[in] obs Observation.
120 120 *
121   - * Assign one observation to another.
  121 + * Assign observation.
122 122 ***************************************************************************/
123 123 GObservation& GObservation::operator= (const GObservation& obs)
124 124 {
... ... @@ -152,7 +152,7 @@ GObservation&amp; GObservation::operator= (const GObservation&amp; obs)
152 152 *
153 153 * @param[in] models Model descriptor.
154 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 157 * @exception GException::gradient_par_mismatch
158 158 * Dimension of gradient vector mismatches number of parameters.
... ... @@ -161,21 +161,30 @@ GObservation&amp; GObservation::operator= (const GObservation&amp; obs)
161 161 * model gives the probability for an event to occur with a given instrument
162 162 * direction, at a given energy and at a given time. The gradient is the
163 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 168 double GObservation::model(const GModels& models, const GEvent& event,
166 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 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 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 189 // Loop over models
181 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 195 // Compute value and add to model
187 196 model += models[i].eval_gradients(event, *this);
188 197  
189   - // Optionally set gradient vector
  198 + // Optionally determine model gradients
190 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 211 // Return
209 212 return model;
... ... @@ -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 220 * @param[in] models Models.
217 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 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 242 // Initialise
226 243 double npred = 0.0; // Reset predicted number of counts
227 244 int igrad = 0; // Reset gradient counter
... ... @@ -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 464 * @brief Returns parameter gradient of Npred
406 465 *
407 466 * @param[in] model Gamma-ray source model.
... ... @@ -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 729 = Npred gradient integration methods =
629 730 = =
630 731 ==========================================================================*/
... ...