vmmlib  1.7.0
 All Classes Namespaces Functions Pages
matrix_pseudoinverse.hpp
1 /*
2  * Copyright (c) 2006-2014, Visualization and Multimedia Lab,
3  * University of Zurich <http://vmml.ifi.uzh.ch>,
4  * Eyescale Software GmbH,
5  * Blue Brain Project, EPFL
6  *
7  * This file is part of VMMLib <https://github.com/VMML/vmmlib/>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are met:
11  *
12  * Redistributions of source code must retain the above copyright notice, this
13  * list of conditions and the following disclaimer. Redistributions in binary
14  * form must reproduce the above copyright notice, this list of conditions and
15  * the following disclaimer in the documentation and/or other materials provided
16  * with the distribution. Neither the name of the Visualization and Multimedia
17  * Lab, University of Zurich nor the names of its contributors may be used to
18  * endorse or promote products derived from this software without specific prior
19  * written permission.
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 #ifndef __VMML__MATRIX_PSEUDOINVERSE__HPP__
33 #define __VMML__MATRIX_PSEUDOINVERSE__HPP__
34 
35 
36 #include <vmmlib/matrix.hpp>
37 #include <vmmlib/vector.hpp>
38 #include <cstddef>
39 #include <functional>
40 #include <vmmlib/lapack_svd.hpp>
41 #include <vmmlib/blas_dgemm.hpp>
42 
43 /*
44  *** computes the pseudo inverse of a non-square matrix ***
45  - the pseudo inverse is computed by the help of SVD
46  - the tolerance for the significant singular values is optionally set
47  - implementation works only for matrices with more rows than columns or quadratic
48  matrices. use a transposed input matrix for matrices with more columns than rows
49  */
50 
51 namespace vmml {
52  // T - vmml::matrix<...> or compatible
53  // Tinternal - float or double
54 
55  template< typename T, typename Tinternal = double >
57  public:
58  typedef typename T::value_type Texternal;
59 
64 
66 
69 
72 
75 
76  struct tmp_matrices {
78  vec_n_type sigmas;
79  matrix_nn_type Vt;
80  matrix_mn_type input;
81 
82  matrix_nm_type result;
83  pinv_type pseudoinverse;
84  matrix_nm_type tmp;
85  };
86 
89  vec_m_type sigmas;
90  matrix_mm_type Vt;
91  matrix_nm_type input;
92 
93  matrix_mn_type result;
94  matrix_mn_type tmp;
95  };
96 
97  void operator()(const T& input, T& pseudoinverse_transposed) {
98 
99 
100  if (T::ROWS < T::COLS) {
101  //cols > rows
102  compute_inv(input, pseudoinverse_transposed);
103  } else {
104  //rows >= cols
105  compute(input, pseudoinverse_transposed);
106  }
107  }
108 
109  void compute_inv(const T& input, T& pseudoinverse_transposed,
110  typename T::value_type tolerance = std::numeric_limits< typename T::value_type >::epsilon()) {
111  if (_work_inv == 0) {
112  _work_inv = new tmp_matrices_inv();
113  }
114 
115  // perform an SVD on the matrix to get the singular values
116  svd_type_inv svd;
117 
118  matrix_nm_type& U = _work_inv->U;
119  vec_m_type& sigmas = _work_inv->sigmas;
120  matrix_mm_type& Vt = _work_inv->Vt;
121  matrix_nm_type& in_data = _work_inv->input;
122  in_data.cast_from(transpose(input));
123 
124  bool svd_ok = svd.compute(in_data, U, sigmas, Vt); // FIXME it always gives bad error code
125 
126  if (!svd_ok) {
127  VMMLIB_ERROR("matrix compute_pseudoinverse - problem with lapack svd.", VMMLIB_HERE);
128  }
129 
130  /*std::cout << "U: " << std::endl << U << std::endl
131  << " sigmas: " << std::endl << sigmas << std::endl
132  << " Vt: " << std::endl << Vt << std::endl;*/
133 
134  // get the number of significant singular, i.e., values which are above the tolerance value
135  typename vector< T::ROWS, Tinternal >::const_iterator it = sigmas.begin(), it_end = sigmas.end();
136  size_t num_sigmas = 0;
137  for (; it != it_end; ++it) {
138  if (*it >= tolerance)
139  ++num_sigmas;
140  else
141  return;
142  }
143 
144  //compute inverse with all the significant inverse singular values
145  matrix_mn_type& result = _work_inv->result;
146  result.zero();
147 
148  matrix_mn_type& tmp = _work_inv->tmp;
149 
150  sigmas.reciprocal_safe();
151 
152  vec_m_type vt_i;
153  vec_n_type u_i;
154  blas_type_inv blas_dgemm1;
155 
156  if (num_sigmas >= 1) {
157 
158  it = sigmas.begin();
159  for (size_t i = 0; i < num_sigmas && it != it_end; ++it, ++i) {
160  Vt.get_row(i, vt_i);
161  U.get_column(i, u_i);
162  blas_dgemm1.compute_vv_outer(vt_i, u_i, tmp);
163 
164  tmp *= *it;
165  result += tmp;
166 
167  }
168  pseudoinverse_transposed.cast_from(result);
169 
170  } else {
171  pseudoinverse_transposed.zero(); //return matrix with zeros
172  }
173  }
174 
175  void compute(const T& input, T& pseudoinverse_transposed,
176  typename T::value_type tolerance = std::numeric_limits< typename T::value_type >::epsilon()) {
177  if (_work == 0) {
178  _work = new tmp_matrices();
179  }
180 
181  // perform an SVD on the matrix to get the singular values
182  svd_type svd;
183 
184  matrix_mn_type& U = _work->U;
185  vec_n_type& sigmas = _work->sigmas;
186  matrix_nn_type& Vt = _work->Vt;
187  matrix_mn_type& in_data = _work->input;
188  in_data.cast_from(input);
189 
190  bool svd_ok = svd.compute(in_data, U, sigmas, Vt); // FIXME it always gives bad error code
191 
192  if (!svd_ok) {
193  VMMLIB_ERROR("matrix compute_pseudoinverse - problem with lapack svd.", VMMLIB_HERE);
194  }
195 
196  /*std::cout << "U: " << std::endl << U << std::endl
197  << " sigmas: " << std::endl << sigmas << std::endl
198  << " Vt: " << std::endl << Vt << std::endl;*/
199 
200  // get the number of significant singular, i.e., values which are above the tolerance value
201 
202  typename vector< T::COLS, Tinternal >::const_iterator it = sigmas.begin(), it_end = sigmas.end();
203  size_t num_sigmas = 0;
204  for (; it != it_end; ++it) {
205  if (*it >= tolerance)
206  ++num_sigmas;
207  else
208  return;
209  }
210 
211  //compute inverse with all the significant inverse singular values
212  matrix_nm_type& result = _work->result;
213  result.zero();
214 
215  pinv_type& pseudoinverse = _work->pseudoinverse;
216  matrix_nm_type& tmp = _work->tmp;
217 
218  sigmas.reciprocal_safe();
219  //double sigma_inv = 0;
220 
221  vec_n_type vt_i;
222  vec_m_type u_i;
223  blas_type blas_dgemm1;
224 
225  if (num_sigmas >= 1) {
226 
227  it = sigmas.begin();
228  for (size_t i = 0; i < num_sigmas && it != it_end; ++it, ++i) {
229  Vt.get_row(i, vt_i);
230  U.get_column(i, u_i);
231  blas_dgemm1.compute_vv_outer(vt_i, u_i, tmp);
232 
233  tmp *= *it;
234  result += tmp;
235 
236  }
237  pseudoinverse.cast_from(result);
238  pseudoinverse.transpose_to(pseudoinverse_transposed);
239 
240  } else {
241  pseudoinverse_transposed.zero(); //return matrix with zeros
242  }
243  }
244 
245  compute_pseudoinverse()
246  : _work(0), _work_inv(0) {
247  }
248 
249  compute_pseudoinverse(const compute_pseudoinverse& )
250  : _work(0), _work_inv(0) {
251  }
252 
253  ~compute_pseudoinverse() {
254  delete _work;
255  delete _work_inv;
256  }
257 
258 
259  protected:
260  tmp_matrices* _work;
261  tmp_matrices_inv* _work_inv;
262 
263  }; //end compute_pseudoinverse class
264 
265 
266 
267 
268 }// end vmml namespace
269 
270 #endif