Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
UnscentedTransform.cpp
1 
2 /* This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4 Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10 BIAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14 
15 BIAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19 
20 You should have received a copy of the GNU Lesser General Public License
21 along with BIAS; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 #include <Base/Common/W32Compat.hh>
25 #include <MathAlgo/UnscentedTransform.hh>
26 #include <MathAlgo/SVD.hh>
27 #include <MathAlgo/Lapack.hh>
28 #include <Base/Common/CompareFloatingPoint.hh>
29 #include <Base/Debug/Exception.hh>
30 #include <Base/Math/Matrix.hh>
31 
32 using namespace BIAS;
33 using namespace std;
34 
37  : UncertaintyTransformBase(), Alpha_(0.001), Beta_(2.0), Kappa_(0.0),
38  UseSVD_(true)
39 {
40  NewDebugLevel("D_UT_SIGMA_POINTS");
41  NewDebugLevel("D_UT_TRANSFORM");
42  NewDebugLevel("D_UT_PARAM");
43 }
44 
45 
48 {}
49 
50 
52 Transform(const Vector<double>& src_mean, const Matrix<double>& src_cov,
53  Vector<double>& dst_mean, Matrix<double>& dst_cov) const
54 {
55  // BIASABORT;
56 
57  if (src_cov.Trace()<numeric_limits<double>::epsilon()){
58  // BIASWARN("Zero covariance matrix detected.");
59  if (Transform_(src_mean, dst_mean) <0) {
60  BCDOUT(D_UT_TRANSFORM, "transformation of mean failed, returning -1");
61  return -1;
62  }
63  const int dim = dst_mean.Size();
64  dst_cov.newsize(dim, dim);
65  dst_cov.SetZero();
66  } else {
67  vector<WeightedSigmaPoint> sigma_points;
68 
69  // check result dimension
70  int res = 0;
71  if ((res = Transform_(src_mean, dst_mean))!=0){
72  BCDOUT(D_UT_TRANSFORM, "transformation of mean failed, returning -1");
73  return -1;
74  } else {
75  BCDOUT(D_UT_TRANSFORM, "transformation of mean yielded "<<dst_mean);
76  }
77 #ifdef BIAS_DEBUG
78  // transform returned 0, so results MUST NOT contain any invalid numbers!
79  for (int d=0; d<dst_mean.size(); d++) {
80  // NaN ?
81  INFNANCHECK(dst_mean[d]);
82  }
83 #endif
84 
85  // compute the sigma points
86  if (ComputeSigmaPoints_(src_mean, src_cov, sigma_points)!=0) return 1;
87 
88  const int dim = dst_mean.Size();
89  // initialize result
90  dst_mean.SetZero();
91  dst_cov.newsize(dim, dim);
92  dst_cov.SetZero();
93 
94  Vector<double> diff, src;
95  vector<WeightedSigmaPoint>::iterator it;
96  for (it=sigma_points.begin(); it!=sigma_points.end(); it++){
97  // transform the sigma points
98  src = it->point;
99  BCDOUT(D_UT_TRANSFORM, "src: "<<src<<endl);
100  if ((res = Transform_(src, it->point))!=0){ return 1; }
101 #ifdef BIAS_DEBUG
102  // transform returned 0, so results MUST NOT contain any invalid numbers!
103  for (int d=0; d<dst_mean.size(); d++) {
104  INFNANCHECK(it->point[d]);
105  }
106 #endif
107 
108  BCDOUT(D_UT_TRANSFORM, "dst: "<<it->point<<endl);
109  // compute weighted mean
110  dst_mean += (it->point * it->weight_mean);
111  BCDOUT(D_UT_TRANSFORM, "mean: "<<dst_mean<<endl);
112  }
113 
114  for (it=sigma_points.begin(); it!=sigma_points.end(); it++){
115  // compute the covariance
116  diff = it->point - dst_mean;
117  BCDOUT(D_UT_TRANSFORM, "diff: "<<diff<<endl);
118  BCDOUT(D_UT_TRANSFORM, "weight "<< it->weight_cov
119  << " * outer product: " << diff.OuterProduct(diff) << endl);
120  dst_cov += it->weight_cov * diff.OuterProduct(diff);
121  BCDOUT(D_UT_TRANSFORM, "cov: "<<dst_cov<<endl);
122  }
123  // now we check finally whether cov is valid
124  for (int row=0; row<dst_cov.num_rows(); row++) {
125  for (int col=0; col<dst_cov.num_cols(); col++) {
126  if (BIAS_ISNAN(dst_cov[row][col]) || BIAS_ISINF(dst_cov[row][col])) {
127  // though each individual point was good, cov could not be computed !
128  return 1;
129  }
130  }
131  }
132  }
133  return 0;
134 }
135 
136 
139  const Matrix<double>& src_cov,
140  std::vector<WeightedSigmaPoint>& sigma_points) const
141 {
142  const int dim = src_mean.Size();
143  const double dbl_dim = static_cast<double>(dim);
144  const double alpha_square = Alpha_ * Alpha_;
145  const double lambda = alpha_square * ( dbl_dim + Kappa_ ) - dbl_dim;
146  //const double dim_plus_lambda = dbl_dim + lambda;
147  const double dim_plus_lambda = alpha_square * ( dbl_dim + Kappa_ );
148  BIASASSERT(fabs(dim_plus_lambda)>DBL_EPSILON);
149  //cout << "dim_plus_lambda="<<dim_plus_lambda<<endl;
150  BCDOUT(D_UT_PARAM, "Alpha: "<<Alpha_<<" Beta : "<<Beta_
151  <<" Kappa: "<<Kappa_<<endl);
152  sigma_points.resize(2 * dim + 1);
153 
154 
155 #ifdef BIAS_DEBUG
156  // check if input matrix contains junk
157  for (int r=0; r<src_cov.num_rows(); r++) {
158  INFNANCHECK(src_mean[r]);
159  for (int c=0; c<src_cov.num_cols(); c++) {
160  INFNANCHECK(src_cov[r][c]);
161  }
162  }
163 #endif
164 
165 
166 
167  SVD svd;
168  Matrix<double> sqrt_cov;
169 
170  // numerically very unstable without normalization !!!
171 #define UT_NORMALIZE
172 #ifdef UT_NORMALIZE
173  double factor = 0.0;
174  for (int i=0; i<dim; i++) {
175 #ifdef BIAS_DEBUG
176  // covariance matrix cannot have negative diagonal elements
177  // this problem cannot be solved here, but must be solved in the
178  // calling class !
179  // BIASASSERT(src_cov[i][i]>=0.0);
180 
181 #endif
182  factor += src_cov[i][i];
183  }
184 
185  if (Less(fabs(factor), numeric_limits<double>::epsilon())){
186  BEXCEPTION("cannot normalize zero covariance matrix "<<src_cov);
187  }
188 
189  Matrix<double> normalized_cov = src_cov / factor;
190  if (UseSVD_) {
191  sqrt_cov = svd.SqrtT(normalized_cov);
192  //cout << "cov: "<<normalized_cov<<"\n sqrt * sqrt = "
193  // <<sqrt_cov*sqrt_cov.Transpose()<<endl;
194  } else {
195  // enforce positive definiteness by adding epsilon to diagonal elements
196  const int num=normalized_cov.GetCols();
197  const double eps_fac = 2.0* numeric_limits<double>::epsilon();
198  for (int i=0; i<num; i++)
199  normalized_cov[i][i] += eps_fac;
200  if (Lapack_Cholesky_SymmetricPositiveDefinit(normalized_cov,
201  sqrt_cov, 'L')!=0){
202 #ifdef BIAS_DEBUG
203  BIASERR("error computing matrix sqrt for "<<normalized_cov<<endl);
204 #endif
205  return -1;
206  }
207  //cout << "cov: "<<normalized_cov<<"\n sqrt * sqrt = "
208  // <<sqrt_cov*sqrt_cov.Transpose()<<endl;
209  }
210  factor *= dim_plus_lambda;
211  factor = sqrt(factor);
212 #else
213  if (UseSVD_){
214  sqrt_cov = svd.SqrtT(dim_plus_lambda * src_cov);
215  } else {
216  Matrix<double> tmp = dim_plus_lambda * src_cov;
217  // enforce positive definiteness by adding epsilon to diagonal elements
218  const int num=tmp.GetCols();
219  const double eps_fac = 2.0* numeric_limits<double>::epsilon();
220  for (int i=0; i<num; i++)
221  tmp[i][i] += eps_fac;
222  if (Lapack_Cholesky_SymmetricPositiveDefinit(tmp, sqrt_cov, 'L')!=0){
223 #ifdef BIAS_DEBUG
224  BIASERR("error computing matrix sqrt for "<<tmp<<endl);
225 #endif
226  return -1;
227  }
228  }
229 #endif
230 
231  if (sqrt_cov.num_cols()==0) {
232 #ifdef BIAS_DEBUG
233  BIASERR("could not compute square root of cov "<< src_cov);
234 #endif
235  return -1;
236  // sqrt_cov = src_cov;
237  // sqrt_cov.SetZero();
238  // //BIASASSERT(sqrt_cov.num_cols()!=10);
239  // for (unsigned int i=0; (int)i<sqrt_cov.num_cols(); i++) {
240  // sqrt_cov[i][i] = 1e-10;
241  // }
242  }
243 
244  BIASASSERT(sqrt_cov.num_cols() == src_cov.num_cols());
245 
246 #ifdef BIAS_DEBUG
247  // check if factorization succeeded or whether we have junk data in matrix
248  for (int r=0; r<sqrt_cov.num_rows(); r++) {
249  for (int c=0; c<sqrt_cov.num_cols(); c++) {
250  INFNANCHECK(sqrt_cov[r][c]);
251  }
252  }
253 #endif
254 
255  Vector<double> vec;
256 
257  sigma_points[0].point = src_mean;
258  sigma_points[0].weight_mean = lambda / dim_plus_lambda;
259  sigma_points[0].weight_cov =
260  lambda / dim_plus_lambda + 1.0 - alpha_square + Beta_;
261 #ifdef BIAS_DEBUG
262  INFNANCHECK(sigma_points[0].weight_mean);
263  INFNANCHECK(sigma_points[0].weight_cov);
264  for (int ii=0; ii<sigma_points[0].point.size(); ii++){
265  INFNANCHECK(sigma_points[0].point[ii]);
266  }
267 #endif
268  int ind;
269  for (int i=0; i<dim; i++){
270  ind = i+1;
271 
272  vec = sqrt_cov.GetCol(i);
273 #ifdef UT_NORMALIZE
274  vec *= factor;
275 #endif
276  sigma_points[ind].point = src_mean + vec;
277  sigma_points[ind+dim].point = src_mean - vec;
278  sigma_points[ind].weight_mean =
279  sigma_points[ind+dim].weight_mean =
280  sigma_points[ind].weight_cov =
281  sigma_points[ind+dim].weight_cov =
282  1.0 / ( 2.0 * dim_plus_lambda );
283 #ifdef BIAS_DEBUG
284  INFNANCHECK(sigma_points[ind].weight_mean);
285  INFNANCHECK(sigma_points[ind].weight_cov);
286  for (int ii=0; ii<sigma_points[ind].point.size(); ii++){
287  INFNANCHECK(sigma_points[ind].point[ii]);
288  INFNANCHECK(sigma_points[ind+dim].point[ii]);
289  }
290 #endif
291  }
292 #ifdef BIAS_DEBUG
293  if (DebugLevelIsSet("D_UT_SIGMA_POINTS")){
294  for (int k=0; k<2; k++){
295  cout << "sp["<<k<<"] = "<<sigma_points[k].point<<" W_m = "
296  <<sigma_points[k].weight_mean << endl<<" W_c = "
297  <<sigma_points[k].weight_cov << endl;
298  }
299  }
300 
301 #endif
302  return 0;
303 }
bool Less(const T left, const T right, const T eps=std::numeric_limits< T >::epsilon())
comparison function for floating point values
computes and holds the singular value decomposition of a rectangular (not necessarily quadratic) Matr...
Definition: SVD.hh:92
Subscript num_cols() const
Definition: cmat.h:320
Matrix< T > & newsize(Subscript M, Subscript N)
Definition: cmat.h:269
Vector< T > GetCol(const int &col) const
return a copy of column &quot;col&quot;, zero based counting
Definition: Matrix.cpp:252
Matrix< double > SqrtT()
returns the square root of a symmetric positive definite matrix M A = sqrt(M) = U * sqrt(S)...
Definition: SVD.cpp:344
unsigned int Size() const
length of the vector
Definition: Vector.hh:143
void SetZero()
equivalent to matrix call
Definition: Vector.hh:156
int Lapack_Cholesky_SymmetricPositiveDefinit(const BIAS::Matrix< double > &A, BIAS::Matrix< double > &UL, const char ul)
Coputes the Cholesky decomposition of a symmetric positive definit matrix A.
Definition: Lapack.cpp:212
bool DebugLevelIsSet(const long int lv) const
Definition: Debug.hh:341
Matrix< T > OuterProduct(const Vector< T > &v) const
outer product, constructs a matrix.
Definition: Vector.cpp:99
void SetZero()
Sets all values to zero.
Definition: Matrix.hh:856
int Transform(const Vector< double > &src_mean, const Matrix< double > &src_cov, Vector< double > &dst_mean, Matrix< double > &dst_cov) const
computes the second order approximation of the transformations of the mean and the associated covaria...
long int NewDebugLevel(const std::string &name)
creates a new debuglevel
Definition: Debug.hh:474
unsigned int GetCols() const
Definition: Matrix.hh:204
base class for all uncertainty transforms
double Alpha_
the alpha parameter determines the spread of the sigma points
T Trace() const
Definition: Matrix.hh:903
double Beta_
beta is used to incorporate prior knowledge of the distribution of x.
Subscript num_rows() const
Definition: cmat.h:319
virtual int Transform_(const Vector< double > &src, Vector< double > &dst) const =0
implements a point transformation
double Kappa_
kappa is a secondary scaling parameter.
int ComputeSigmaPoints_(const Vector< double > &src_mean, const Matrix< double > &src_cov, std::vector< WeightedSigmaPoint > &sigma_points) const
The covariance of a n-dimensional vector is approximated using 2n+1 so called sigma points with assoc...
Subscript size() const
Definition: vec.h:262