TooN
Cholesky.h
1 // -*- c++ -*-
2 
3 // Copyright (C) 2009 Tom Drummond (twd20@cam.ac.uk)
4 
5 //All rights reserved.
6 //
7 //Redistribution and use in source and binary forms, with or without
8 //modification, are permitted provided that the following conditions
9 //are met:
10 //1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 //
16 //THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
17 //AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 //IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 //ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
20 //LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 //SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 //INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 //CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 //ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 //POSSIBILITY OF SUCH DAMAGE.
27 
28 
29 #ifndef TOON_INCLUDE_CHOLESKY_H
30 #define TOON_INCLUDE_CHOLESKY_H
31 
32 #include <TooN/TooN.h>
33 
34 namespace TooN {
35 
36 
66 template <int Size=Dynamic, class Precision=DefaultPrecision>
67 class Cholesky {
68 public:
69  Cholesky(){}
70 
74  template<class P2, class B2>
76  : my_cholesky(m) {
77  SizeMismatch<Size,Size>::test(m.num_rows(), m.num_cols());
78  do_compute();
79  }
80 
82  Cholesky(int size) : my_cholesky(size,size) {}
83 
84 
87  template<class P2, class B2> void compute(const Matrix<Size, Size, P2, B2>& m){
88  SizeMismatch<Size,Size>::test(m.num_rows(), m.num_cols());
89  SizeMismatch<Size,Size>::test(m.num_rows(), my_cholesky.num_rows());
90  my_cholesky=m;
91  do_compute();
92  }
93 
94  private:
95  void do_compute() {
96  int size=my_cholesky.num_rows();
97  for(int col=0; col<size; col++){
98  Precision inv_diag = 1;
99  for(int row=col; row < size; row++){
100  // correct for the parts of cholesky already computed
101  Precision val = my_cholesky(row,col);
102  for(int col2=0; col2<col; col2++){
103  // val-=my_cholesky(col,col2)*my_cholesky(row,col2)*my_cholesky(col2,col2);
104  val-=my_cholesky(col2,col)*my_cholesky(row,col2);
105  }
106  if(row==col){
107  // this is the diagonal element so don't divide
108  my_cholesky(row,col)=val;
109  if(val == 0){
110  my_rank = row;
111  return;
112  }
113  inv_diag=1/val;
114  } else {
115  // cache the value without division in the upper half
116  my_cholesky(col,row)=val;
117  // divide my the diagonal element for all others
118  my_cholesky(row,col)=val*inv_diag;
119  }
120  }
121  }
122  my_rank = size;
123  }
124 
125  public:
126 
129  template<int Size2, class P2, class B2>
131  int size=my_cholesky.num_rows();
132  SizeMismatch<Size,Size2>::test(size, v.size());
133 
134  // first backsub through L
135  Vector<Size, Precision> y(size);
136  for(int i=0; i<size; i++){
137  Precision val = v[i];
138  for(int j=0; j<i; j++){
139  val -= my_cholesky(i,j)*y[j];
140  }
141  y[i]=val;
142  }
143 
144  // backsub through diagonal
145  for(int i=0; i<size; i++){
146  y[i]/=my_cholesky(i,i);
147  }
148 
149  // backsub through L.T()
150  Vector<Size,Precision> result(size);
151  for(int i=size-1; i>=0; i--){
152  Precision val = y[i];
153  for(int j=i+1; j<size; j++){
154  val -= my_cholesky(j,i)*result[j];
155  }
156  result[i]=val;
157  }
158 
159  return result;
160  }
161 
164  template<int Size2, int C2, class P2, class B2>
166  int size=my_cholesky.num_rows();
167  SizeMismatch<Size,Size2>::test(size, m.num_rows());
168 
169  // first backsub through L
170  Matrix<Size, C2, Precision> y(size, m.num_cols());
171  for(int i=0; i<size; i++){
172  Vector<C2, Precision> val = m[i];
173  for(int j=0; j<i; j++){
174  val -= my_cholesky(i,j)*y[j];
175  }
176  y[i]=val;
177  }
178 
179  // backsub through diagonal
180  for(int i=0; i<size; i++){
181  y[i]*=(1/my_cholesky(i,i));
182  }
183 
184  // backsub through L.T()
185  Matrix<Size,C2,Precision> result(size, m.num_cols());
186  for(int i=size-1; i>=0; i--){
187  Vector<C2,Precision> val = y[i];
188  for(int j=i+1; j<size; j++){
189  val -= my_cholesky(j,i)*result[j];
190  }
191  result[i]=val;
192  }
193  return result;
194  }
195 
196 
199  // easy way to get inverse - could be made more efficient
201  Matrix<Size,Size,Precision>I(Identity(my_cholesky.num_rows()));
202  return backsub(I);
203  }
204 
206  Precision determinant(){
207  Precision answer=my_cholesky(0,0);
208  for(int i=1; i<my_cholesky.num_rows(); i++){
209  answer*=my_cholesky(i,i);
210  }
211  return answer;
212  }
213 
214  template <int Size2, typename P2, typename B2>
215  Precision mahalanobis(const Vector<Size2, P2, B2>& v) const {
216  return v * backsub(v);
217  }
218 
219  Matrix<Size,Size,Precision> get_unscaled_L() const {
220  Matrix<Size,Size,Precision> m(my_cholesky.num_rows(),
221  my_cholesky.num_rows());
222  m=Identity;
223  for (int i=1;i<my_cholesky.num_rows();i++) {
224  for (int j=0;j<i;j++) {
225  m(i,j)=my_cholesky(i,j);
226  }
227  }
228  return m;
229  }
230 
231  Matrix<Size,Size,Precision> get_D() const {
232  Matrix<Size,Size,Precision> m(my_cholesky.num_rows(),
233  my_cholesky.num_rows());
234  m=Zeros;
235  for (int i=0;i<my_cholesky.num_rows();i++) {
236  m(i,i)=my_cholesky(i,i);
237  }
238  return m;
239  }
240 
241  Matrix<Size,Size,Precision> get_L() const {
242  using std::sqrt;
243  Matrix<Size,Size,Precision> m(my_cholesky.num_rows(),
244  my_cholesky.num_rows());
245  m=Zeros;
246  for (int j=0;j<my_cholesky.num_cols();j++) {
247  Precision sqrtd=sqrt(my_cholesky(j,j));
248  m(j,j)=sqrtd;
249  for (int i=j+1;i<my_cholesky.num_rows();i++) {
250  m(i,j)=my_cholesky(i,j)*sqrtd;
251  }
252  }
253  return m;
254  }
255 
256  int rank() const { return my_rank; }
257 
258 private:
259  Matrix<Size,Size,Precision> my_cholesky;
260  int my_rank;
261 };
262 
263 
264 }
265 
266 #endif
Decomposes a positive-semidefinite symmetric matrix A (such as a covariance) into L*D*L^T...
Definition: Cholesky.h:67
Pretty generic SFINAE introspection generator.
Definition: vec_test.cc:21
Precision determinant()
Compute the determinant.
Definition: Cholesky.h:206
A matrix.
Definition: matrix.hh:105
Vector< Size, Precision > backsub(const Vector< Size2, P2, B2 > &v) const
Compute x = A^-1*v Run time is O(N^2)
Definition: Cholesky.h:130
Cholesky(const Matrix< Size, Size, P2, B2 > &m)
Construct the Cholesky decomposition of a matrix.
Definition: Cholesky.h:75
Cholesky(int size)
Constructor for Size=Dynamic.
Definition: Cholesky.h:82
Matrix< Size, C2, Precision > backsub(const Matrix< Size2, C2, P2, B2 > &m) const
overload
Definition: Cholesky.h:165
void compute(const Matrix< Size, Size, P2, B2 > &m)
Compute the LDL^T decomposition of another matrix.
Definition: Cholesky.h:87
Matrix< Size, Size, Precision > get_inverse()
Compute A^-1 and store in M Run time is O(N^3)
Definition: Cholesky.h:200
Matrix< R, C, P > sqrt(const Matrix< R, C, P, B > &m)
computes a matrix square root of a matrix m by the product form of the Denman and Beavers iteration a...
Definition: helpers.h:350
Definition: size_mismatch.hh:103