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