Commit 6017f6708bbe55603ec5632c3997e1500437ea20

Authored by Jürgen Knödlseder
1 parent a133c400

Restructure GModel::eval() method for vectorised computations and implement a st…

…ack for model parameters with analytical gradients at the level of the GObservation class.
include/GModel.hpp
... ... @@ -121,16 +121,14 @@ public:
121 121 virtual double eval(const GEvent& event,
122 122 const GObservation& obs,
123 123 const bool& gradients = false) const = 0;
  124 + virtual GVector eval(const GObservation& obs,
  125 + GMatrixSparse* gradients = NULL) const = 0;
124 126 virtual double npred(const GEnergy& obsEng, const GTime& obsTime,
125 127 const GObservation& obs) const = 0;
126 128 virtual void read(const GXmlElement& xml) = 0;
127 129 virtual void write(GXmlElement& xml) const = 0;
128 130 virtual std::string print(const GChatter& chatter = NORMAL) const = 0;
129 131  
130   - // Virtual methods
131   - virtual GVector eval(const GObservation& obs,
132   - GMatrixSparse* gradients = NULL) const;
133   -
134 132 // Implemented methods
135 133 int size(void) const;
136 134 int scales(void) const;
... ...
include/GModelData.hpp
1 1 /***************************************************************************
2 2 * GModelData.hpp - Abstract virtual data model class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2011-2016 by Juergen Knoedlseder *
  4 + * copyright (C) 2011-2020 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -40,6 +40,8 @@ class GEvents;
40 40 class GObservation;
41 41 class GRan;
42 42 class GXmlElement;
  43 +class GVector;
  44 +class GMatrixSparse;
43 45  
44 46  
45 47 /***********************************************************************//**
... ... @@ -78,6 +80,10 @@ public:
78 80 virtual void write(GXmlElement& xml) const = 0;
79 81 virtual std::string print(const GChatter& chatter = NORMAL) const = 0;
80 82  
  83 + // Implemented pure virtual base class methods
  84 + virtual GVector eval(const GObservation& obs,
  85 + GMatrixSparse* gradients = NULL) const;
  86 +
81 87 protected:
82 88 // Protected methods
83 89 void init_members(void);
... ...
include/GModelSky.hpp
... ... @@ -40,7 +40,6 @@
40 40 #include "GPhoton.hpp"
41 41 #include "GPhotons.hpp"
42 42 #include "GRan.hpp"
43   -#include "GVector.hpp"
44 43 #include "GEvent.hpp"
45 44 #include "GObservation.hpp"
46 45 #include "GXmlElement.hpp"
... ... @@ -48,6 +47,8 @@
48 47 /* __ Forward declarations _______________________________________________ */
49 48 class GEvent;
50 49 class GObservation;
  50 +class GVector;
  51 +class GMatrixSparse;
51 52  
52 53  
53 54 /***********************************************************************//**
... ... @@ -149,6 +150,8 @@ public:
149 150 virtual double eval(const GEvent& event,
150 151 const GObservation& obs,
151 152 const bool& gradients = false) const;
  153 + virtual GVector eval(const GObservation& obs,
  154 + GMatrixSparse* gradients = NULL) const;
152 155 virtual double npred(const GEnergy& obsEng,
153 156 const GTime& obsTime,
154 157 const GObservation& obs) const;
... ... @@ -156,10 +159,6 @@ public:
156 159 virtual void write(GXmlElement& xml) const;
157 160 virtual std::string print(const GChatter& chatter = NORMAL) const;
158 161  
159   - // Reimplemented virtual base class methods
160   - virtual GVector eval(const GObservation& obs,
161   - GMatrixSparse* gradients = NULL) const;
162   -
163 162 // Other methods
164 163 GModelSpatial* spatial(void) const;
165 164 GModelSpectral* spectral(void) const;
... ... @@ -182,6 +181,7 @@ protected:
182 181 void free_members(void);
183 182 void set_pointers(void);
184 183 void set_type(void);
  184 + void signal_analytical_gradients(const GObservation& obs) const;
185 185 GModelSpatial* xml_spatial(const GXmlElement& spatial) const;
186 186 GModelSpectral* xml_spectral(const GXmlElement& spectral) const;
187 187 GModelTemporal* xml_temporal(const GXmlElement& temporal) const;
... ...
include/GObservation.hpp
... ... @@ -29,17 +29,18 @@
29 29  
30 30 /* __ Includes ___________________________________________________________ */
31 31 #include <string>
  32 +#include <vector>
32 33 #include "GBase.hpp"
33 34 #include "GEvents.hpp"
34 35 #include "GTime.hpp"
35 36 #include "GFunction.hpp"
  37 +#include "GModelPar.hpp"
36 38  
37 39 /* __ Forward declarations _______________________________________________ */
38 40 class GVector;
39 41 class GMatrixSparse;
40 42 class GModel;
41 43 class GModels;
42   -class GModelPar;
43 44 class GResponse;
44 45 class GXmlElement;
45 46  
... ... @@ -119,12 +120,14 @@ public:
119 120  
120 121 // Implemented methods
121 122 bool has_events(void) const;
  123 + bool has_gradient(const GModelPar& par) const;
122 124 void name(const std::string& name);
123 125 void id(const std::string& id);
124 126 void statistic(const std::string& statistic);
125 127 const std::string& name(void) const;
126 128 const std::string& id(void) const;
127 129 const std::string& statistic(void) const;
  130 + void computed_gradient(const GModelPar& par) const;
128 131  
129 132 protected:
130 133 // Protected methods
... ... @@ -217,6 +220,9 @@ protected:
217 220 std::string m_id; //!< Observation identifier
218 221 std::string m_statistic; //!< Optimizer statistic
219 222 GEvents* m_events; //!< Pointer to event container
  223 +
  224 + // Stack of pointers to spatial parameters with gradients
  225 + mutable std::vector<GModelPar*> m_spat_pars_with_gradients;
220 226 };
221 227  
222 228  
... ...
pyext/GModel.i
... ... @@ -53,6 +53,8 @@ public:
53 53 virtual double eval(const GEvent& event,
54 54 const GObservation& obs,
55 55 const bool& gradients = false) const = 0;
  56 + virtual GVector eval(const GObservation& obs,
  57 + GMatrixSparse* gradients = NULL) const = 0;
56 58 virtual double npred(const GEnergy& obsEng, const GTime& obsTime,
57 59 const GObservation& obs) const = 0;
58 60 virtual void read(const GXmlElement& xml) = 0;
... ...
pyext/GModelData.i
1 1 /***************************************************************************
2 2 * GModelData.i - Abstract virtual data model class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2011-2016 by Juergen Knoedlseder *
  4 + * copyright (C) 2011-2020 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -57,6 +57,10 @@ public:
57 57 virtual GEvents* mc(const GObservation& obs, GRan& ran) const = 0;
58 58 virtual void read(const GXmlElement& xml) = 0;
59 59 virtual void write(GXmlElement& xml) const = 0;
  60 +
  61 + // Implemented pure virtual base class methods
  62 + virtual GVector eval(const GObservation& obs,
  63 + GMatrixSparse* gradients = NULL) const;
60 64 };
61 65  
62 66  
... ...
pyext/GModelSky.i
1 1 /***************************************************************************
2 2 * GModelSky.i - Sky model class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2011-2018 by Juergen Knoedlseder *
  4 + * copyright (C) 2011-2020 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -63,6 +63,8 @@ public:
63 63 virtual double eval(const GEvent& event,
64 64 const GObservation& obs,
65 65 const bool& gradients = false) const;
  66 + virtual GVector eval(const GObservation& obs,
  67 + GMatrixSparse* gradients = NULL) const;
66 68 virtual double npred(const GEnergy& obsEng,
67 69 const GTime& obsTime,
68 70 const GObservation& obs) const;
... ...
pyext/GObservation.i
1 1 /***************************************************************************
2 2 * GObservation.i - Abstract observation base class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2008-2019 by Juergen Knoedlseder *
  4 + * copyright (C) 2008-2020 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -94,12 +94,14 @@ public:
94 94  
95 95 // Implemented methods
96 96 bool has_events(void) const;
  97 + bool has_gradient(const GModelPar& par) const;
97 98 void name(const std::string& name);
98 99 void id(const std::string& id);
99 100 void statistic(const std::string& statistic);
100 101 const std::string& name(void) const;
101 102 const std::string& id(void) const;
102 103 const std::string& statistic(void) const;
  104 + void computed_gradient(const GModelPar& par) const;
103 105 };
104 106  
105 107  
... ...
src/model/GModel.cpp
... ... @@ -31,13 +31,9 @@
31 31 #include "GTools.hpp"
32 32 #include "GException.hpp"
33 33 #include "GModel.hpp"
34   -#include "GVector.hpp"
35   -#include "GMatrixSparse.hpp"
36   -#include "GObservation.hpp"
37 34  
38 35 /* __ Method name definitions ____________________________________________ */
39 36 #define G_ACCESS "GModel::operator[](std::string&)"
40   -#define G_EVAL "GModel::eval(GObservation&, GMatrixSparse*)"
41 37 #define G_AT "GModel::at(int&)"
42 38 #define G_SCALE "GModelPar& GModel::scale(int&)"
43 39 #define G_WRITE_SCALES "GModel::write_scales(GXmlElement&)"
... ... @@ -225,106 +221,6 @@ const GModelPar&amp; GModel::operator[](const std::string&amp; name) const
225 221 ==========================================================================*/
226 222  
227 223 /***********************************************************************//**
228   - * @brief Return model values and gradients
229   - *
230   - * @param[in] obs Observation.
231   - * @param[out] gradients Pointer to matrix of gradients.
232   - * @return Model values.
233   - *
234   - * Evaluates the model values and parameter gradients for all events in an
235   - * observation. Gradients are only returned if the @p gradients pointer is
236   - * not NULL.
237   - *
238   - * The matrix of gradients is a sparse matrix where the number of rows
239   - * corresponds to the number of model parameters and the number of columns
240   - * corresponds to the number of events. An exception is thrown if the
241   - * dimension of the @p gradients matrix is not compatible with the model
242   - * and the observations.
243   - ***************************************************************************/
244   -GVector GModel::eval(const GObservation& obs,
245   - GMatrixSparse* gradients) const
246   -{
247   - // Get number of model parameters and number of events
248   - int npars = size();
249   - int nevents = obs.events()->size();
250   -
251   - // Initialise gradients flag
252   - bool grad = (gradients != NULL);
253   -
254   - // Check matrix consistency
255   - if (grad) {
256   - if (gradients->columns() != npars) {
257   - std::string msg = "Number of "+gammalib::str(gradients->columns())+
258   - " columns in gradient matrix differs from number "
259   - "of "+gammalib::str(npars)+" parameters "
260   - "in model. Please provide a compatible gradient "
261   - "matrix.";
262   - throw GException::invalid_argument(G_EVAL, msg);
263   - }
264   - if (gradients->rows() != nevents) {
265   - std::string msg = "Number of "+gammalib::str(gradients->rows())+
266   - " rows in gradient matrix differs from number "
267   - "of "+gammalib::str(nevents)+" events in "
268   - "observation. Please provide a compatible "
269   - "gradient matrix.";
270   - throw GException::invalid_argument(G_EVAL, msg);
271   - }
272   - }
273   -
274   - // Initialise values
275   - GVector values(nevents);
276   -
277   - // Initialise temporary vectors to hold gradients
278   - GVector* tmp_gradients = new GVector[npars];
279   - for (int i = 0; i < npars; ++i) {
280   - tmp_gradients[i] = GVector(nevents);
281   - }
282   -
283   - // Loop over events
284   - for (int k = 0; k < nevents; ++k) {
285   -
286   - // Get reference to event
287   - const GEvent& event = *((*obs.events())[k]);
288   -
289   - // Evaluate probability
290   - values[k] = eval(event, obs, grad);
291   -
292   - // Set gradients
293   - if (grad) {
294   -
295   - // Initialise gradient vector
296   - //GVector gradient(npars);
297   -
298   - // Extract relevant parameter gradients
299   - for (int i = 0; i < npars; ++i) {
300   - const GModelPar& par = (*this)[i];
301   - if (par.is_free() && par.has_grad()) {
302   - //gradient[i] = par.factor_gradient();
303   - tmp_gradients[i][k] = par.factor_gradient();
304   - }
305   - }
306   -
307   - // Insert gradient vector into matrix
308   - //gradients->add_to_column(k, gradient);
309   -
310   - } // endif: looped over gradients
311   -
312   - } // endfor: looped over events
313   -
314   - // Fill gradients into matrix
315   - for (int i = 0; i < npars; ++i) {
316   - gradients->column(i, tmp_gradients[i]);
317   - }
318   -
319   - // Delete temporal gradients
320   - delete [] tmp_gradients;
321   -
322   - // Return values
323   - return values;
324   -}
325   -
326   -
327   -/***********************************************************************//**
328 224 * @brief Returns reference to model parameter by index
329 225 *
330 226 * @param[in] index Parameter index [0,...,size()-1].
... ...
src/model/GModelData.cpp
1 1 /***************************************************************************
2   - * GModelData.cpp - Abstract virtual data model class *
  2 + * GModelData.cpp - Abstract virtual data model class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2011 by Jurgen Knodlseder *
  4 + * copyright (C) 2011-2020 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -21,7 +21,7 @@
21 21 /**
22 22 * @file GModelData.cpp
23 23 * @brief GModelData class implementation.
24   - * @author J. Knodlseder
  24 + * @author Juergen Knoedlseder
25 25 */
26 26  
27 27 /* __ Includes ___________________________________________________________ */
... ... @@ -31,8 +31,12 @@
31 31 #include "GTools.hpp"
32 32 #include "GException.hpp"
33 33 #include "GModelData.hpp"
  34 +#include "GVector.hpp"
  35 +#include "GMatrixSparse.hpp"
  36 +#include "GObservation.hpp"
34 37  
35 38 /* __ Method name definitions ____________________________________________ */
  39 +#define G_EVAL "GModel::eval(GObservation&, GMatrixSparse*)"
36 40  
37 41 /* __ Macros _____________________________________________________________ */
38 42  
... ... @@ -116,8 +120,9 @@ GModelData::~GModelData(void)
116 120 * @brief Assignment operator
117 121 *
118 122 * @param[in] model Data model.
  123 + * @return Data model.
119 124 ***************************************************************************/
120   -GModelData& GModelData::operator= (const GModelData& model)
  125 +GModelData& GModelData::operator=(const GModelData& model)
121 126 {
122 127 // Execute only if object is not identical
123 128 if (this != &model) {
... ... @@ -147,6 +152,120 @@ GModelData&amp; GModelData::operator= (const GModelData&amp; model)
147 152 = =
148 153 ==========================================================================*/
149 154  
  155 +/***********************************************************************//**
  156 + * @brief Return model values and gradients
  157 + *
  158 + * @param[in] obs Observation.
  159 + * @param[out] gradients Pointer to matrix of gradients.
  160 + * @return Model values.
  161 + *
  162 + * Evaluates the model values and parameter gradients for all events in an
  163 + * observation. Gradients are only returned if the @p gradients pointer is
  164 + * not NULL.
  165 + *
  166 + * The matrix of gradients is a sparse matrix where the number of rows
  167 + * corresponds to the number of model parameters and the number of columns
  168 + * corresponds to the number of events. An exception is thrown if the
  169 + * dimension of the @p gradients matrix is not compatible with the model
  170 + * and the observations.
  171 + ***************************************************************************/
  172 +GVector GModelData::eval(const GObservation& obs,
  173 + GMatrixSparse* gradients) const
  174 +{
  175 + // Get number of model parameters and number of events
  176 + int npars = size();
  177 + int nevents = obs.events()->size();
  178 +
  179 + // Initialise gradients flag
  180 + bool grad = ((gradients != NULL) && (npars > 0));
  181 +
  182 + // Check matrix consistency
  183 + if (grad) {
  184 + if (gradients->columns() != npars) {
  185 + std::string msg = "Number of "+gammalib::str(gradients->columns())+
  186 + " columns in gradient matrix differs from number "
  187 + "of "+gammalib::str(npars)+" parameters "
  188 + "in model. Please provide a compatible gradient "
  189 + "matrix.";
  190 + throw GException::invalid_argument(G_EVAL, msg);
  191 + }
  192 + if (gradients->rows() != nevents) {
  193 + std::string msg = "Number of "+gammalib::str(gradients->rows())+
  194 + " rows in gradient matrix differs from number "
  195 + "of "+gammalib::str(nevents)+" events in "
  196 + "observation. Please provide a compatible "
  197 + "gradient matrix.";
  198 + throw GException::invalid_argument(G_EVAL, msg);
  199 + }
  200 + }
  201 +
  202 + // Initialise values
  203 + GVector values(nevents);
  204 +
  205 + // Initialise temporary vectors to hold gradients
  206 + GVector* tmp_gradients = NULL;
  207 + if (grad) {
  208 +
  209 + // Allocate temporary vectors to hold the gradients
  210 + tmp_gradients = new GVector[npars];
  211 + for (int i = 0; i < npars; ++i) {
  212 + tmp_gradients[i] = GVector(nevents);
  213 + }
  214 +
  215 + // Signal all parameters that will have analytical gradients. These
  216 + // are all parameters that are free and for which the model provides
  217 + // analytical gradients.
  218 + for (int i = 0; i < npars; ++i) {
  219 + const GModelPar& par = (*this)[i];
  220 + if (par.is_free() && par.has_grad()) {
  221 + obs.computed_gradient(par);
  222 + }
  223 + }
  224 +
  225 + } // endif: gradients were requested
  226 +
  227 + // Loop over events
  228 + for (int k = 0; k < nevents; ++k) {
  229 +
  230 + // Get reference to event
  231 + const GEvent& event = *((*obs.events())[k]);
  232 +
  233 + // Evaluate probability
  234 + values[k] = eval(event, obs, grad);
  235 +
  236 + // Set gradients
  237 + if (grad) {
  238 +
  239 + // Extract relevant parameter gradients
  240 + for (int i = 0; i < npars; ++i) {
  241 + const GModelPar& par = (*this)[i];
  242 + if (par.is_free() && par.has_grad()) {
  243 + tmp_gradients[i][k] = par.factor_gradient();
  244 + }
  245 + }
  246 +
  247 + } // endif: looped over gradients
  248 +
  249 + } // endfor: looped over events
  250 +
  251 + // Post-process gradients
  252 + if (grad) {
  253 +
  254 + // Fill gradients into matrix
  255 + for (int i = 0; i < npars; ++i) {
  256 + gradients->column(i, tmp_gradients[i]);
  257 + }
  258 +
  259 + // Delete temporal gradients
  260 + delete [] tmp_gradients;
  261 +
  262 + } // endif: post-processed gradients
  263 +
  264 + // Return values
  265 + return values;
  266 +}
  267 +
  268 +
150 269 /*==========================================================================
151 270 = =
152 271 = Private methods =
... ... @@ -183,10 +302,3 @@ void GModelData::free_members(void)
183 302 // Return
184 303 return;
185 304 }
186   -
187   -
188   -/*==========================================================================
189   - = =
190   - = Friends =
191   - = =
192   - ==========================================================================*/
... ...
src/model/GModelSky.cpp
... ... @@ -568,6 +568,12 @@ double GModelSky::eval(const GEvent&amp; event,
568 568 // Evaluate model
569 569 double value = obs.response()->convolve(*this, event, obs, gradients);
570 570  
  571 + // If gradients were requested then signal all computed analytical
  572 + // gradients
  573 + if (gradients) {
  574 + signal_analytical_gradients(obs);
  575 + }
  576 +
571 577 // Return
572 578 return value;
573 579 }
... ... @@ -592,6 +598,12 @@ GVector GModelSky::eval(const GObservation&amp; obs,
592 598 // Evaluate model
593 599 GVector values = obs.response()->convolve(*this, obs, gradients);
594 600  
  601 + // If gradients were requested then signal all computed analytical
  602 + // gradients
  603 + if (gradients) {
  604 + signal_analytical_gradients(obs);
  605 + }
  606 +
595 607 // Return
596 608 return values;
597 609 }
... ... @@ -1276,6 +1288,63 @@ void GModelSky::set_type(void)
1276 1288  
1277 1289  
1278 1290 /***********************************************************************//**
  1291 + * @brief Signal all parameters that have analytical gradients for a given
  1292 + * observation
  1293 + *
  1294 + * @param[in] obs Observation.
  1295 + *
  1296 + * This method signals all spectral, temporal and model scaling parameters
  1297 + * for which analytical gradients exist for a given observation. Analytical
  1298 + * gradients will be signalled for all of these parameters that are free
  1299 + * and for which `has_grad()` is `true`.
  1300 + *
  1301 + * Note that spatial analytical gradients are excluded. These will be
  1302 + * signalled by the underlying convolution method as analytical gradients
  1303 + * are not (yet) supported by all convolution methods.
  1304 + ***************************************************************************/
  1305 +void GModelSky::signal_analytical_gradients(const GObservation& obs) const
  1306 +{
  1307 + // If the model has a spectral component then signal all free parameters
  1308 + // with analytical gradients
  1309 + if (spectral() != NULL) {
  1310 + for (int i = 0; i < spectral()->size(); ++i) {
  1311 + GModelPar& par = (*(spectral()))[i];
  1312 + if (par.is_free() && par.has_grad()) {
  1313 + obs.computed_gradient(par);
  1314 + }
  1315 + }
  1316 + }
  1317 +
  1318 + // If the model has a temporal component then signal all free parameters
  1319 + // with analytical gradients
  1320 + if (temporal() != NULL) {
  1321 + for (int i = 0; i < temporal()->size(); ++i) {
  1322 + GModelPar& par = (*(temporal()))[i];
  1323 + if (par.is_free() && par.has_grad()) {
  1324 + obs.computed_gradient(par);
  1325 + }
  1326 + }
  1327 + }
  1328 +
  1329 + // If the model has a scales then signal all free parameters with
  1330 + // analytical gradients
  1331 + if (has_scales()) {
  1332 + for (int i = 0; i < scales(); ++i) {
  1333 + const GModelPar& par = scale(i);
  1334 + if (par.name() == obs.instrument()) {
  1335 + if (par.is_free() && par.has_grad()) {
  1336 + obs.computed_gradient(par);
  1337 + }
  1338 + }
  1339 + }
  1340 + }
  1341 +
  1342 + // Return
  1343 + return;
  1344 +}
  1345 +
  1346 +
  1347 +/***********************************************************************//**
1279 1348 * @brief Return pointer to spatial model from XML element
1280 1349 *
1281 1350 * @param[in] spatial XML element.
... ...
src/obs/GObservation.cpp
... ... @@ -161,6 +161,72 @@ GObservation&amp; GObservation::operator=(const GObservation&amp; obs)
161 161 ==========================================================================*/
162 162  
163 163 /***********************************************************************//**
  164 + * @brief Return events
  165 + *
  166 + * @exception GException::no_events
  167 + * No events allocated for observation.
  168 + *
  169 + * Returns pointer to events.
  170 + ***************************************************************************/
  171 +GEvents* GObservation::events(void)
  172 +{
  173 + // Throw an exception if the event container is not valid
  174 + if (m_events == NULL) {
  175 + std::string msg = "No events allocated for observation.";
  176 + throw GException::invalid_value(G_EVENTS, msg);
  177 + }
  178 +
  179 + // Return pointer to events
  180 + return (m_events);
  181 +}
  182 +
  183 +
  184 +/***********************************************************************//**
  185 + * @brief Return events (const version)
  186 + *
  187 + * @exception GException::no_events
  188 + * No events allocated for observation.
  189 + *
  190 + * Returns const pointer to events.
  191 + ***************************************************************************/
  192 +const GEvents* GObservation::events(void) const
  193 +{
  194 + // Throw an exception if the event container is not valid
  195 + if (m_events == NULL) {
  196 + std::string msg = "No events allocated for observation.";
  197 + throw GException::invalid_value(G_EVENTS, msg);
  198 + }
  199 +
  200 + // Return pointer to events
  201 + return (m_events);
  202 +}
  203 +
  204 +
  205 +/***********************************************************************//**
  206 + * @brief Set event container
  207 + *
  208 + * @param[in] events Event container.
  209 + *
  210 + * Set the event container for this observation by cloning the container
  211 + * specified in the argument.
  212 + ***************************************************************************/
  213 +void GObservation::events(const GEvents& events)
  214 +{
  215 + // Free existing event container only if it differs from current event
  216 + // container. This prevents unintential deallocation of the argument
  217 + if ((m_events != NULL) && (m_events != &events)) {
  218 + delete m_events;
  219 + }
  220 +
  221 + // Clone events
  222 + m_events = events.clone();
  223 +
  224 + // Return
  225 + return;
  226 +}
  227 +
  228 +
  229 +/***********************************************************************//**
164 230 * @brief Compute likelihood function
165 231 *
166 232 * @param[in] models Models.
... ... @@ -330,7 +396,7 @@ double GObservation::model(const GModels&amp; models,
330 396  
331 397 // Set gradient
332 398 if (par.is_free()) {
333   - if (par.has_grad()) {
  399 + if (has_gradient(par)) {
334 400 (*gradient)[igrad+ipar] = par.factor_gradient();
335 401 }
336 402 else {
... ... @@ -356,7 +422,7 @@ double GObservation::model(const GModels&amp; models,
356 422  
357 423 // Set gradient
358 424 if (par.is_free()) {
359   - if (par.has_grad()) {
  425 + if (has_gradient(par)) {
360 426 (*gradient)[igrad+ipar] = par.factor_gradient();
361 427 }
362 428 else {
... ... @@ -463,11 +529,9 @@ GVector GObservation::model(const GModels&amp; models,
463 529 else {
464 530  
465 531 // Allocate gradient matrix
466   - //GMatrixSparse gradients(mptr->size(), nevents);
467 532 GMatrixSparse gradients(nevents, mptr->size());
468 533  
469 534 // Initialise sparse matrix stack
470   - //gradients.stack_init(mptr->size()*1000, 1000);
471 535 gradients.stack_init(nevents*mptr->size(), mptr->size());
472 536  
473 537 // Evaluate model and add to values
... ... @@ -495,14 +559,11 @@ GVector GObservation::model(const GModels&amp; models,
495 559  
496 560 // Determine gradient
497 561 GVector grad(nevents);
498   - if (par.is_free()) {
499   - if (par.has_grad()) {
500   - //grad = gradients.row(ipar);
501   - grad = gradients.column(ipar);
502   - }
503   - else {
504   - grad = model_grad(*mptr, par);
505   - }
  562 + if (has_gradient(par)) {
  563 + grad = gradients.column(ipar);
  564 + }
  565 + else {
  566 + grad = model_grad(*mptr, par);
506 567 }
507 568  
508 569 // Set gradient
... ... @@ -523,10 +584,8 @@ GVector GObservation::model(const GModels&amp; models,
523 584  
524 585 // Determine gradient
525 586 GVector grad(nevents);
526   - if (par.is_free()) {
527   - if (par.has_grad()) {
528   - //grad = gradients.row(ipar);
529   - grad = gradients.column(ipar);
  587 + if (has_gradient(par)) {
  588 + grad = gradients.column(ipar);
530 589 // Debugging
531 590 /*
532 591 if (ipar < 3) {
... ... @@ -542,10 +601,9 @@ if (ipar &lt; 3) {
542 601 }
543 602 }
544 603 */
545   - }
546   - else {
547   - grad = model_grad(*mptr, par);
548   - }
  604 + }
  605 + else {
  606 + grad = model_grad(*mptr, par);
549 607 }
550 608  
551 609 // Set gradient
... ... @@ -1110,68 +1168,65 @@ void GObservation::remove_response_cache(const std::string&amp; name)
1110 1168  
1111 1169  
1112 1170 /***********************************************************************//**
1113   - * @brief Set event container
  1171 + * @brief Check whether a model parameter has an analytical gradient
1114 1172 *
1115   - * @param[in] events Event container.
  1173 + * @param[in] par Model parameter.
  1174 + * @returns True if model parameter is free and has an analytical gradient.
1116 1175 *
1117   - * Set the event container for this observation by cloning the container
1118   - * specified in the argument.
  1176 + * Checks whether a model parameter is free and has an analytical gradient.
1119 1177 ***************************************************************************/
1120   -void GObservation::events(const GEvents& events)
  1178 +bool GObservation::has_gradient(const GModelPar& par) const
1121 1179 {
1122   - // Free existing event container only if it differs from current event
1123   - // container. This prevents unintential deallocation of the argument
1124   - if ((m_events != NULL) && (m_events != &events)) {
1125   - delete m_events;
1126   - }
  1180 + // Initialise flag
  1181 + bool has_gradient = false;
  1182 +
  1183 + // Only consider free parameters
  1184 + if (par.is_free()) {
1127 1185  
1128   - // Clone events
1129   - m_events = events.clone();
  1186 + // Search for parameter address in stack and set flag to true if
  1187 + // parameter address was found
  1188 + for (int i = 0; i < m_spat_pars_with_gradients.size(); ++i) {
  1189 + if (m_spat_pars_with_gradients[i] == &par) {
  1190 + has_gradient = true;
  1191 + break;
  1192 + }
  1193 + }
1130 1194  
1131   - // Return
1132   - return;
  1195 + } // endif: parameter was free
  1196 +
  1197 + // Return flag
  1198 + return has_gradient;
1133 1199 }
1134 1200  
1135 1201  
1136 1202 /***********************************************************************//**
1137   - * @brief Return events
  1203 + * @brief Signals that an analytical gradient was computed for a model
  1204 + * parameter
1138 1205 *
1139   - * @exception GException::no_events
1140   - * No events allocated for observation.
  1206 + * @param[in] par Model parameter.
1141 1207 *
1142   - * Returns pointer to events.
  1208 + * Signals that an analytical gradient was computed for a model parameter.
1143 1209 ***************************************************************************/
1144   -GEvents* GObservation::events(void)
  1210 +void GObservation::computed_gradient(const GModelPar& par) const
1145 1211 {
1146   - // Throw an exception if the event container is not valid
1147   - if (m_events == NULL) {
1148   - std::string msg = "No events allocated for observation.";
1149   - throw GException::invalid_value(G_EVENTS, msg);
  1212 + // Initialise flag
  1213 + bool in_stack = false;
  1214 +
  1215 + // Check if parameter is already in stack
  1216 + for (int i = 0; i < m_spat_pars_with_gradients.size(); ++i) {
  1217 + if (m_spat_pars_with_gradients[i] == &par) {
  1218 + in_stack = true;
  1219 + break;
  1220 + }
1150 1221 }
1151 1222  
1152   - // Return pointer to events
1153   - return (m_events);
1154   -}
1155   -
1156   -
1157   -/***********************************************************************//**
1158   - * @brief Return events (const version)
1159   - *
1160   - * @exception GException::no_events
1161   - * No events allocated for observation.
1162   - *
1163   - * Returns const pointer to events.
1164   - ***************************************************************************/
1165   -const GEvents* GObservation::events(void) const
1166   -{
1167   - // Throw an exception if the event container is not valid
1168   - if (m_events == NULL) {
1169   - std::string msg = "No events allocated for observation.";
1170   - throw GException::invalid_value(G_EVENTS, msg);
  1223 + // If parameter is not yet in stack the push it on the stack
  1224 + if (!in_stack) {
  1225 + m_spat_pars_with_gradients.push_back(const_cast<GModelPar*>(&par));
1171 1226 }
1172 1227  
1173   - // Return pointer to events
1174   - return (m_events);
  1228 + // Return
  1229 + return;
1175 1230 }
1176 1231  
1177 1232  
... ... @@ -1192,6 +1247,9 @@ void GObservation::init_members(void)
1192 1247 m_statistic = "cstat";
1193 1248 m_events = NULL;
1194 1249  
  1250 + // Initialise stack of pointers to spatial parameters with gradients
  1251 + m_spat_pars_with_gradients.clear();
  1252 +
1195 1253 // Return
1196 1254 return;
1197 1255 }
... ... @@ -1211,6 +1269,9 @@ void GObservation::copy_members(const GObservation&amp; obs)
1211 1269 m_id = obs.m_id;
1212 1270 m_statistic = obs.m_statistic;
1213 1271  
  1272 + // Copy stack of pointers to spatial parameters with gradients
  1273 + m_spat_pars_with_gradients = obs.m_spat_pars_with_gradients;
  1274 +
1214 1275 // Clone members
1215 1276 m_events = (obs.m_events != NULL) ? obs.m_events->clone() : NULL;
1216 1277  
... ...