npstat is hosted by Hepforge, IPPP Durham
NPStat  5.10.0
AbsUnfold1D.hh
Go to the documentation of this file.
1 #ifndef NPSTAT_ABSUNFOLD1D_HH_
2 #define NPSTAT_ABSUNFOLD1D_HH_
3 
4 /*!
5 // \file AbsUnfold1D.hh
6 //
7 // \brief Interface definition for 1-d unfolding algorithms
8 //
9 // Author: I. Volobouev
10 //
11 // February 2014
12 */
13 
14 #include <vector>
15 #include <utility>
16 #include <cassert>
17 #include <stdexcept>
18 
19 #include "npstat/nm/Matrix.hh"
20 
21 #ifdef SWIG
22 #include "npstat/wrap/NumpyTypecode.hh"
23 #endif // SWIG
24 
25 namespace npstat {
26  class LocalPolyFilter1D;
27 
29  {
30  public:
31  /**
32  // Number of rows of the response matrix should be equal
33  // to the number of discretization intervals in the
34  // space of observations. Number of columns should be equal
35  // to the number of intervals in the original (unfolded) space.
36  */
37  explicit AbsUnfold1D(const Matrix<double>& responseMatrix);
38 
39  inline virtual ~AbsUnfold1D() {}
40 
41  inline const Matrix<double>& responseMatrix() const
42  {return responseMatrix_;}
43 
44  inline const Matrix<double>& transposedResponseMatrix() const
45  {return responseMatrixT_;}
46 
47  inline const std::vector<double>& efficiency() const
48  {return efficiency_;}
49 
50  //@{
51  /**
52  // The first element of the pair is the entropic nDoF and
53  // the second is the nDoF based on the trace
54  */
55  std::pair<double, double> smoothingNDoF() const;
56  std::pair<double, double> responseNDoF() const;
57  std::pair<double, double> smoothedResponseNDoF() const;
58  //@}
59 
60  /**
61  // Effective number of degrees of freedom for the smearing model
62  // (for use in AIC, etc). Will call "unfold" internally, so can't
63  // be a const method.
64  */
65  std::pair<double, double> modelNDoF();
66 
67  /** Set the initial approximation to the unfolded solution */
69  const double* approx, unsigned lenApprox);
70 
71  /** Clear the initial approximation to the unfolded solution */
72  virtual void clearInitialApproximation();
73 
74  /** Return the initial approximation to the unfolded solution */
75  virtual const std::vector<double>& getInitialApproximation() const;
76 
77  /**
78  // Set the smoothing filter used. The filter will not be copied.
79  // It is the responsibility of the user of this class to ensure
80  // that the lifetime of the filter exceeds the lifetime of
81  // this object.
82  */
83  virtual void setFilter(const LocalPolyFilter1D* f);
84 
85  /** Retrieve the smoothing filter used */
86  virtual const LocalPolyFilter1D* getFilter(bool throwIfNull=false) const;
87 
88  /** Switch between using filtering or convolution */
89  inline virtual void useConvolutions(const bool b) {useConvolutions_ = b;}
90 
91  /** Check if the filter should use "filter" or "convolve" method */
92  inline bool usingConvolutions() const {return useConvolutions_;}
93 
94  /**
95  // Method to be implemented by derived classes. "lenObserved"
96  // and "lenUnfolded" should be consistent with the dimensionality
97  // of the response matrix provided in the constructor. The
98  // "observationCovarianceMatrix" pointer to the covariance matrix
99  // of observations can be NULL in which case this covariance
100  // matrix will be evaluated internally (typically, assuming
101  // either Poisson or multinomial distributions for the observed
102  // counts). "unfoldedCovarianceMatrix" pointer, if not NULL,
103  // should be used to return the covariance matrix of unfolded
104  // values with dimensions (lenUnfolded x lenUnfolded). This
105  // function should return "true" on success and "false" on failure.
106  */
107  virtual bool unfold(const double* observed, unsigned lenObserved,
108  const Matrix<double>* observationCovarianceMatrix,
109  double* unfolded, unsigned lenUnfolded,
110  Matrix<double>* unfoldedCovarianceMatrix) = 0;
111 
112  /**
113  // An L1-like relative distance between two unnormalized
114  // density-like arrays. Can be used as a convergence measure.
115  */
116  static double probDelta(const double* prev, const double* next,
117  const unsigned len);
118 
119  /**
120  // Procedure for building covariance matrix of observations
121  // according to either Poisson or multinomial distribution
122  */
124  const double* yhat, unsigned lenObserved, bool isMultinomial);
125 
126  protected:
127  // Build uniform initial approximation to the unfolded solution.
128  // This is useful if the initial approximation was not set explicitly.
129  void buildUniformInitialApproximation(const double* observed,
130  unsigned lenObserved,
131  std::vector<double>* result) const;
132 
133  // The following function will throw the std::invalid_argument
134  // exception if the dimensions are incompatible with those of the
135  // response matrix
136  void validateDimensions(unsigned lenObserved, unsigned lenUnfold) const;
137 
138  // The following function will throw the std::invalid_argument
139  // exception if the input array has negative entries or if all
140  // values are 0.
141  static void validateDensity(const double* observ,
142  unsigned lenObserved);
143  private:
144  // Disable copy and assignment because using the same
145  // filter by two different objects of this type is not
146  // necessarily cool
147  AbsUnfold1D();
148  AbsUnfold1D(const AbsUnfold1D&);
149  AbsUnfold1D& operator=(const AbsUnfold1D&);
150 
151  const Matrix<double> responseMatrix_;
152  const Matrix<double> responseMatrixT_;
153  std::vector<double> efficiency_;
154  std::vector<double> initialApproximation_;
155  const LocalPolyFilter1D* filt_;
156  bool useConvolutions_;
157 
158 #ifdef SWIG
159  public:
160  inline const LocalPolyFilter1D* getFilter2() const
161  {
162  return this->getFilter(true);
163  }
164  static inline double probDelta2(const double* prev, unsigned prevlen,
165  const double* next, unsigned nextlen)
166  {
167  if (prevlen != nextlen) throw std::runtime_error(
168  "In npstat::AbsUnfold1D::probDelta2: incompatible inputs");
169  return probDelta(prev, next, nextlen);
170  }
171  inline PyObject* unfold2(const double* observed, unsigned lenObserved,
172  Matrix<double>* covMatOut)
173  {
174  assert(covMatOut);
175  const int typenum = NumpyTypecode<double>::code;
176  npy_intp sh = responseMatrix_.nColumns();
177  PyObject* array = PyArray_SimpleNew(1, &sh, typenum);
178  double* result = (double*)PyArray_DATA((PyArrayObject*)array);
179  try
180  {
181  const bool status = this->unfold(observed, lenObserved, 0,
182  result, sh, covMatOut);
183  if (!status) throw std::runtime_error(
184  "In npstat::AbsUnfold1D::unfold2: "
185  "expectation-maximization iterations failed to converge");
186  }
187  catch (const std::exception& e)
188  {
189  Py_DECREF(array);
190  throw;
191  }
192  return array;
193  }
194  inline PyObject* unfold2(const double* observed, unsigned lenObserved,
195  const Matrix<double>& observationCovMat,
196  Matrix<double>* covMatOut)
197  {
198  assert(covMatOut);
199  const int typenum = NumpyTypecode<double>::code;
200  npy_intp sh = responseMatrix_.nColumns();
201  PyObject* array = PyArray_SimpleNew(1, &sh, typenum);
202  double* result = (double*)PyArray_DATA((PyArrayObject*)array);
203  try
204  {
205  const bool status = this->unfold(observed, lenObserved,
206  &observationCovMat,
207  result, sh, covMatOut);
208  if (!status) throw std::runtime_error(
209  "In npstat::AbsUnfold1D::unfold2: "
210  "expectation-maximization iterations failed to converge");
211  }
212  catch (const std::exception& e)
213  {
214  Py_DECREF(array);
215  throw;
216  }
217  return array;
218  }
219  inline PyObject* unfoldNoCov(const double* observed, unsigned lenObserved)
220  {
221  const int typenum = NumpyTypecode<double>::code;
222  npy_intp sh = responseMatrix_.nColumns();
223  PyObject* array = PyArray_SimpleNew(1, &sh, typenum);
224  double* result = (double*)PyArray_DATA((PyArrayObject*)array);
225  try
226  {
227  const bool status = this->unfold(observed, lenObserved, 0,
228  result, sh, 0);
229  if (!status) throw std::runtime_error(
230  "In npstat::AbsUnfold1D::unfoldNoCov: "
231  "expectation-maximization iterations failed to converge");
232  }
233  catch (const std::exception& e)
234  {
235  Py_DECREF(array);
236  throw;
237  }
238  return array;
239  }
240  inline PyObject* unfoldNoCov(const double* observed, unsigned lenObserved,
241  const Matrix<double>& observationCovMat)
242  {
243  const int typenum = NumpyTypecode<double>::code;
244  npy_intp sh = responseMatrix_.nColumns();
245  PyObject* array = PyArray_SimpleNew(1, &sh, typenum);
246  double* result = (double*)PyArray_DATA((PyArrayObject*)array);
247  try
248  {
249  const bool status = this->unfold(observed, lenObserved,
250  &observationCovMat,
251  result, sh, 0);
252  if (!status) throw std::runtime_error(
253  "In npstat::AbsUnfold1D::unfoldNoCov: "
254  "expectation-maximization iterations failed to converge");
255  }
256  catch (const std::exception& e)
257  {
258  Py_DECREF(array);
259  throw;
260  }
261  return array;
262  }
263  inline PyObject* fold(const double* unfolded, unsigned lenUnfolded) const
264  {
265  if (lenUnfolded != responseMatrix_.nColumns()) throw std::invalid_argument(
266  "In npstat::AbsUnfold1D::fold: incompatible input array size");
267  const int typenum = NumpyTypecode<double>::code;
268  npy_intp sh = responseMatrix_.nRows();
269  PyObject* array = PyArray_SimpleNew(1, &sh, typenum);
270  double* result = (double*)PyArray_DATA((PyArrayObject*)array);
271  responseMatrix_.timesVector(unfolded, lenUnfolded, result, sh);
272  return array;
273  }
274 #endif // SWIG
275  };
276 }
277 
278 #endif // NPSTAT_ABSUNFOLD1D_HH_
Template matrix class.
Definition: AbsUnfold1D.hh:29
bool usingConvolutions() const
Definition: AbsUnfold1D.hh:92
static Matrix< double > observationCovariance(const double *yhat, unsigned lenObserved, bool isMultinomial)
virtual void useConvolutions(const bool b)
Definition: AbsUnfold1D.hh:89
virtual void setFilter(const LocalPolyFilter1D *f)
virtual void setInitialApproximation(const double *approx, unsigned lenApprox)
virtual bool unfold(const double *observed, unsigned lenObserved, const Matrix< double > *observationCovarianceMatrix, double *unfolded, unsigned lenUnfolded, Matrix< double > *unfoldedCovarianceMatrix)=0
virtual void clearInitialApproximation()
std::pair< double, double > smoothingNDoF() const
virtual const std::vector< double > & getInitialApproximation() const
AbsUnfold1D(const Matrix< double > &responseMatrix)
virtual const LocalPolyFilter1D * getFilter(bool throwIfNull=false) const
static double probDelta(const double *prev, const double *next, const unsigned len)
std::pair< double, double > modelNDoF()
Definition: LocalPolyFilter1D.hh:38
unsigned nRows() const
Definition: Matrix.hh:155
Matrix timesVector(const Num2 *data, unsigned dataLen) const
Definition: AbsArrayProjector.hh:14