Commit a6e238ac2fe6708fecc4a1632900134dd240de4e
1 parent
86c3e7da
add model gradient computation method
Showing
2 changed files
with
167 additions
and
36 deletions
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& GObservation::operator= (const GObservation& 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& GObservation::operator= (const GObservation& 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& models, const GEvent& 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& models, const GEvent& 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 | ==========================================================================*/ | ... | ... |