GNU Octave  4.2.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
svd.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2016-2017 CarnĂ« Draug
4 Copyright (C) 1994-2016 John W. Eaton
5 
6 This file is part of Octave.
7 
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <http://www.gnu.org/licenses/>.
21 
22 */
23 
24 #if defined (HAVE_CONFIG_H)
25 # include "config.h"
26 #endif
27 
28 #include "svd.h"
29 
30 #include <cassert>
31 #include <algorithm>
32 
33 #include "CMatrix.h"
34 #include "dDiagMatrix.h"
35 #include "fDiagMatrix.h"
36 #include "dMatrix.h"
37 #include "fCMatrix.h"
38 #include "fMatrix.h"
39 #include "lo-error.h"
40 #include "lo-lapack-proto.h"
41 #include "oct-locbuf.h"
42 
43 namespace octave
44 {
45  namespace math
46  {
47  template <typename T>
48  T
50  {
51  if (m_type == svd::Type::sigma_only)
52  (*current_liboctave_error_handler)
53  ("svd: U not computed because type == svd::sigma_only");
54 
55  return left_sm;
56  }
57 
58  template <typename T>
59  T
61  {
62  if (m_type == svd::Type::sigma_only)
63  (*current_liboctave_error_handler)
64  ("svd: V not computed because type == svd::sigma_only");
65 
66  return right_sm;
67  }
68 
69 
70  // GESVD specializations
71 
72 #define GESVD_REAL_STEP(f, F) \
73  F77_XFCN (f, F, (F77_CONST_CHAR_ARG2 (&jobu, 1), \
74  F77_CONST_CHAR_ARG2 (&jobv, 1), \
75  m, n, tmp_data, m1, s_vec, u, m1, vt, \
76  nrow_vt1, work.data (), lwork, info \
77  F77_CHAR_ARG_LEN (1) \
78  F77_CHAR_ARG_LEN (1)))
79 
80 #define GESVD_COMPLEX_STEP(f, F, CMPLX_ARG) \
81  F77_XFCN (f, F, (F77_CONST_CHAR_ARG2 (&jobu, 1), \
82  F77_CONST_CHAR_ARG2 (&jobv, 1), \
83  m, n, CMPLX_ARG (tmp_data), \
84  m1, s_vec, CMPLX_ARG (u), m1, \
85  CMPLX_ARG (vt), nrow_vt1, \
86  CMPLX_ARG (work.data ()), \
87  lwork, rwork.data (), info \
88  F77_CHAR_ARG_LEN (1) \
89  F77_CHAR_ARG_LEN (1)))
90 
91  // DGESVD
92  template<>
93  void
94  svd<Matrix>::gesvd (char& jobu, char& jobv, octave_idx_type m,
95  octave_idx_type n, double* tmp_data, octave_idx_type m1,
96  double* s_vec, double* u, double* vt,
97  octave_idx_type nrow_vt1, std::vector<double>& work,
98  octave_idx_type& lwork, octave_idx_type& info)
99  {
100  GESVD_REAL_STEP (dgesvd, DGESVD);
101 
102  lwork = work[0];
103  work.reserve (lwork);
104 
105  GESVD_REAL_STEP (dgesvd, DGESVD);
106  }
107 
108  // SGESVD
109  template<>
110  void
111  svd<FloatMatrix>::gesvd (char& jobu, char& jobv, octave_idx_type m,
112  octave_idx_type n, float* tmp_data,
113  octave_idx_type m1, float* s_vec, float* u, float* vt,
114  octave_idx_type nrow_vt1, std::vector<float>& work,
115  octave_idx_type& lwork, octave_idx_type& info)
116  {
117  GESVD_REAL_STEP (sgesvd, SGESVD);
118 
119  lwork = work[0];
120  work.reserve (lwork);
121 
122  GESVD_REAL_STEP (sgesvd, SGESVD);
123  }
124 
125  // ZGESVD
126  template<>
127  void
128  svd<ComplexMatrix>::gesvd (char& jobu, char& jobv, octave_idx_type m,
129  octave_idx_type n, Complex* tmp_data,
130  octave_idx_type m1, double* s_vec, Complex* u,
131  Complex* vt, octave_idx_type nrow_vt1,
132  std::vector<Complex>& work,
133  octave_idx_type& lwork, octave_idx_type& info)
134  {
135  std::vector<double> rwork (5 * std::max (m, n));
136 
137  GESVD_COMPLEX_STEP (zgesvd, ZGESVD, F77_DBLE_CMPLX_ARG);
138 
139  lwork = work[0].real ();
140  work.reserve (lwork);
141 
142  GESVD_COMPLEX_STEP (zgesvd, ZGESVD, F77_DBLE_CMPLX_ARG);
143  }
144 
145  // CGESVD
146  template<>
147  void
148  svd<FloatComplexMatrix>::gesvd (char& jobu, char& jobv,
150  FloatComplex* tmp_data, octave_idx_type m1,
151  float* s_vec, FloatComplex* u,
152  FloatComplex* vt, octave_idx_type nrow_vt1,
153  std::vector<FloatComplex>& work,
154  octave_idx_type& lwork, octave_idx_type& info)
155  {
156  std::vector<float> rwork (5 * std::max (m, n));
157 
158  GESVD_COMPLEX_STEP (cgesvd, CGESVD, F77_CMPLX_ARG);
159 
160  lwork = work[0].real ();
161  work.reserve (lwork);
162 
163  GESVD_COMPLEX_STEP (cgesvd, CGESVD, F77_CMPLX_ARG);
164  }
165 
166 #undef GESVD_REAL_STEP
167 #undef GESVD_COMPLEX_STEP
168 
169 
170  // GESDD specializations
171 
172 #define GESDD_REAL_STEP(f, F) \
173  F77_XFCN (f, F, (F77_CONST_CHAR_ARG2 (&jobz, 1), \
174  m, n, tmp_data, m1, s_vec, u, m1, vt, nrow_vt1, \
175  work.data (), lwork, iwork, info \
176  F77_CHAR_ARG_LEN (1)))
177 
178 #define GESDD_COMPLEX_STEP(f, F, CMPLX_ARG) \
179  F77_XFCN (f, F, (F77_CONST_CHAR_ARG2 (&jobz, 1), m, n, \
180  CMPLX_ARG (tmp_data), m1, \
181  s_vec, CMPLX_ARG (u), m1, \
182  CMPLX_ARG (vt), nrow_vt1, \
183  CMPLX_ARG (work.data ()), lwork, \
184  rwork.data (), iwork, info \
185  F77_CHAR_ARG_LEN (1)))
186 
187  // DGESDD
188  template<>
189  void
191  double* tmp_data, octave_idx_type m1,
192  double* s_vec, double* u,
193  double* vt, octave_idx_type nrow_vt1,
194  std::vector<double>& work, octave_idx_type& lwork,
195  octave_idx_type* iwork, octave_idx_type& info)
196  {
197  GESDD_REAL_STEP (dgesdd, DGESDD);
198 
199  lwork = work[0];
200  work.reserve (lwork);
201 
202  GESDD_REAL_STEP (dgesdd, DGESDD);
203  }
204 
205  // SGESDD
206  template<>
207  void
209  float* tmp_data, octave_idx_type m1,
210  float* s_vec, float* u,
211  float* vt, octave_idx_type nrow_vt1,
212  std::vector<float>& work, octave_idx_type& lwork,
213  octave_idx_type* iwork, octave_idx_type& info)
214  {
215  GESDD_REAL_STEP (sgesdd, SGESDD);
216 
217  lwork = work[0];
218  work.reserve (lwork);
219 
220  GESDD_REAL_STEP (sgesdd, SGESDD);
221  }
222 
223  // ZGESDD
224  template<>
225  void
227  Complex* tmp_data, octave_idx_type m1,
228  double* s_vec, Complex* u,
229  Complex* vt, octave_idx_type nrow_vt1,
230  std::vector<Complex>& work, octave_idx_type& lwork,
231  octave_idx_type* iwork, octave_idx_type& info)
232  {
233 
234  octave_idx_type min_mn = std::min (m, n);
235 
236  octave_idx_type lrwork;
237  if (jobz == 'N')
238  lrwork = 7*min_mn;
239  else
240  lrwork = 5*min_mn*min_mn + 5*min_mn;
241 
242  std::vector<double> rwork (lrwork);
243 
244  GESDD_COMPLEX_STEP (zgesdd, ZGESDD, F77_DBLE_CMPLX_ARG);
245 
246  lwork = work[0].real ();
247  work.reserve (lwork);
248 
249  GESDD_COMPLEX_STEP (zgesdd, ZGESDD, F77_DBLE_CMPLX_ARG);
250  }
251 
252  // CGESDD
253  template<>
254  void
256  octave_idx_type n,
257  FloatComplex* tmp_data, octave_idx_type m1,
258  float* s_vec, FloatComplex* u,
259  FloatComplex* vt, octave_idx_type nrow_vt1,
260  std::vector<FloatComplex>& work,
261  octave_idx_type& lwork, octave_idx_type* iwork,
262  octave_idx_type& info)
263  {
264  octave_idx_type min_mn = std::min (m, n);
265  octave_idx_type max_mn = std::max (m, n);
266 
267  octave_idx_type lrwork;
268  if (jobz == 'N')
269  lrwork = 5*min_mn;
270  else
271  lrwork = min_mn * std::max (5*min_mn+7, 2*max_mn+2*min_mn+1);
272  std::vector<float> rwork (lrwork);
273 
274  GESDD_COMPLEX_STEP (cgesdd, CGESDD, F77_CMPLX_ARG);
275 
276  lwork = work[0].real ();
277  work.reserve (lwork);
278 
279  GESDD_COMPLEX_STEP (cgesdd, CGESDD, F77_CMPLX_ARG);
280  }
281 
282 #undef GESDD_REAL_STEP
283 #undef GESDD_COMPLEX_STEP
284 
285 
286  template<typename T>
288  svd::Driver driver)
289  : m_type (type), m_driver (driver), left_sm (), sigma (), right_sm ()
290  {
291  octave_idx_type info;
292 
293  octave_idx_type m = a.rows ();
294  octave_idx_type n = a.cols ();
295 
296  if (m == 0 || n == 0)
297  {
298  switch (m_type)
299  {
300  case svd::Type::std:
301  left_sm = T (m, m, 0);
302  for (octave_idx_type i = 0; i < m; i++)
303  left_sm.xelem (i, i) = 1;
304  sigma = DM_T (m, n);
305  right_sm = T (n, n, 0);
306  for (octave_idx_type i = 0; i < n; i++)
307  right_sm.xelem (i, i) = 1;
308  break;
309 
310  case svd::Type::economy:
311  left_sm = T (m, 0, 0);
312  sigma = DM_T (0, 0);
313  right_sm = T (0, n, 0);
314  break;
315 
317  default:
318  sigma = DM_T (0, 1);
319  break;
320  }
321  return;
322  }
323 
324  T atmp = a;
325  P* tmp_data = atmp.fortran_vec ();
326 
327  octave_idx_type min_mn = m < n ? m : n;
328 
329  char jobu = 'A';
330  char jobv = 'A';
331 
332  octave_idx_type ncol_u = m;
333  octave_idx_type nrow_vt = n;
334  octave_idx_type nrow_s = m;
335  octave_idx_type ncol_s = n;
336 
337  switch (m_type)
338  {
339  case svd::Type::economy:
340  jobu = jobv = 'S';
341  ncol_u = nrow_vt = nrow_s = ncol_s = min_mn;
342  break;
343 
345 
346  // Note: for this case, both jobu and jobv should be 'N', but
347  // there seems to be a bug in dgesvd from Lapack V2.0. To
348  // demonstrate the bug, set both jobu and jobv to 'N' and find
349  // the singular values of [eye(3), eye(3)]. The result is
350  // [-sqrt(2), -sqrt(2), -sqrt(2)].
351  //
352  // For Lapack 3.0, this problem seems to be fixed.
353 
354  jobu = jobv = 'N';
355  ncol_u = nrow_vt = 1;
356  break;
357 
358  default:
359  break;
360  }
361 
362  if (! (jobu == 'N' || jobu == 'O'))
363  left_sm.resize (m, ncol_u);
364 
365  P* u = left_sm.fortran_vec ();
366 
367  sigma.resize (nrow_s, ncol_s);
368  DM_P* s_vec = sigma.fortran_vec ();
369 
370  if (! (jobv == 'N' || jobv == 'O'))
371  right_sm.resize (nrow_vt, n);
372 
373  P* vt = right_sm.fortran_vec ();
374 
375  // Query _GESVD for the correct dimension of WORK.
376 
377  octave_idx_type lwork = -1;
378 
379  std::vector<P> work (1);
380 
381  octave_idx_type m1 = std::max (m, static_cast<octave_idx_type> (1));
382  octave_idx_type nrow_vt1 = std::max (nrow_vt,
383  static_cast<octave_idx_type> (1));
384 
386  gesvd (jobu, jobv, m, n, tmp_data, m1, s_vec, u, vt, nrow_vt1,
387  work, lwork, info);
388  else if (m_driver == svd::Driver::GESDD)
389  {
390  assert (jobu == jobv);
391  char jobz = jobu;
392 
393  std::vector<octave_idx_type> iwork (8 * std::min (m, n));
394 
395  gesdd (jobz, m, n, tmp_data, m1, s_vec, u, vt, nrow_vt1,
396  work, lwork, iwork.data (), info);
397  }
398  else
399  abort ();
400 
401  if (! (jobv == 'N' || jobv == 'O'))
402  right_sm = right_sm.hermitian ();
403  }
404 
405  // Instantiations we need.
406 
407  template class svd<Matrix>;
408 
409  template class svd<FloatMatrix>;
410 
411  template class svd<ComplexMatrix>;
412 
413  template class svd<FloatComplexMatrix>;
414  }
415 }
Octave interface to the compression and uncompression libraries.
Definition: aepbalance.cc:47
#define F77_DBLE_CMPLX_ARG(x)
Definition: f77-fcn.h:345
void gesvd(char &jobu, char &jobv, octave_idx_type m, octave_idx_type n, P *tmp_data, octave_idx_type m1, DM_P *s_vec, P *u, P *vt, octave_idx_type nrow_vt1, std::vector< P > &work, octave_idx_type &lwork, octave_idx_type &info)
#define GESDD_COMPLEX_STEP(f, F, CMPLX_ARG)
Definition: svd.cc:178
DM_T sigma
Definition: svd.h:99
T left_singular_matrix(void) const
Definition: svd.cc:49
#define GESVD_COMPLEX_STEP(f, F, CMPLX_ARG)
Definition: svd.cc:80
svd::Type m_type
Definition: svd.h:95
T::element_type P
Definition: svd.h:92
u
Definition: lu.cc:138
svd(void)
Definition: svd.h:56
DM_T::element_type DM_P
Definition: svd.h:93
T right_singular_matrix(void) const
Definition: svd.cc:60
calling an anonymous function involves an overhead quite comparable to the overhead of an m file function Passing a handle to a built in function is because the interpreter is not involved in the internal loop For a
Definition: cellfun.cc:398
T::real_diag_matrix_type DM_T
Definition: svd.h:41
nd deftypefn *octave_map m
Definition: ov-struct.cc:2058
void gesdd(char &jobz, octave_idx_type m, octave_idx_type n, P *tmp_data, octave_idx_type m1, DM_P *s_vec, P *u, P *vt, octave_idx_type nrow_vt1, std::vector< P > &work, octave_idx_type &lwork, octave_idx_type *iwork, octave_idx_type &info)
idx type
Definition: ov.cc:3129
#define GESDD_REAL_STEP(f, F)
Definition: svd.cc:172
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:228
#define F77_CMPLX_ARG(x)
Definition: f77-fcn.h:339
octave_idx_type rows(void) const
Definition: oct-map.h:375
=val(i)}if ode{val(i)}occurs in table i
Definition: lookup.cc:239
#define GESVD_REAL_STEP(f, F)
Definition: svd.cc:72
std::complex< float > FloatComplex
Definition: oct-cmplx.h:32
std::complex< double > Complex
Definition: oct-cmplx.h:31
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:205
svd::Driver m_driver
Definition: svd.h:96