SVD.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /* SVD.cpp
  2. *
  3. * Copyright (C) 1994-2018 David Weenink
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /*
  19. djmw 20010719
  20. djmw 20020408 GPL + cosmetic changes.
  21. djmw 20020415 +SVD_synthesize.
  22. djmw 20030624 Removed NRC svd calls.
  23. djmw 20030825 Removed praat_USE_LAPACK external variable.
  24. djmw 20031018 Removed bug in SVD_solve that caused incorrect output when nrow > ncol
  25. djmw 20031101 Changed documentation in SVD_compute + bug correction in SVD_synthesize.
  26. djmw 20031111 Added GSVD_create_d.
  27. djmw 20051201 Adapt for numberOfRows < numberOfColumns
  28. djmw 20060810 Removed #include praat.h
  29. djmw 20061212 Changed info to Melder_writeLine<x> format.
  30. djmw 20070102 Removed the #include "TableOfReal.h"
  31. djmw 20071012 Added: o_CAN_WRITE_AS_ENCODING.h
  32. djmw 20110304 Thing_new
  33. */
  34. #include "SVD.h"
  35. #include "NUMmachar.h"
  36. #include "Collection.h"
  37. #include "NUMclapack.h"
  38. #include "NUMcblas.h"
  39. #include "oo_DESTROY.h"
  40. #include "SVD_def.h"
  41. #include "oo_COPY.h"
  42. #include "SVD_def.h"
  43. #include "oo_EQUAL.h"
  44. #include "SVD_def.h"
  45. #include "oo_CAN_WRITE_AS_ENCODING.h"
  46. #include "SVD_def.h"
  47. #include "oo_WRITE_TEXT.h"
  48. #include "SVD_def.h"
  49. #include "oo_WRITE_BINARY.h"
  50. #include "SVD_def.h"
  51. #include "oo_READ_TEXT.h"
  52. #include "SVD_def.h"
  53. #include "oo_READ_BINARY.h"
  54. #include "SVD_def.h"
  55. #include "oo_DESCRIPTION.h"
  56. #include "SVD_def.h"
  57. void structSVD :: v_info () {
  58. MelderInfo_writeLine (U"Number of rows: ", numberOfRows);
  59. MelderInfo_writeLine (U"Number of columns: ", numberOfColumns);
  60. MelderInfo_writeLine (U"This matrix is", (isTransposed ? U"" : U"not "), U" transposed.");
  61. }
  62. Thing_implement (SVD, Daata, 1);
  63. /*
  64. m >=n, mxn matrix A has svd UDV', where u is mxn, D is n and V is nxn.
  65. m < n, then transpose A. Consider A' with svd (UDV')'= VDU', where v is mxm, D is m and U' is mxn
  66. */
  67. void SVD_init (SVD me, integer numberOfRows, integer numberOfColumns) {
  68. if (numberOfRows < numberOfColumns) {
  69. my isTransposed = true;
  70. integer tmp = numberOfRows; numberOfRows = numberOfColumns; numberOfColumns = tmp;
  71. }
  72. my numberOfRows = numberOfRows;
  73. my numberOfColumns = numberOfColumns;
  74. if (! NUMfpp) {
  75. NUMmachar ();
  76. }
  77. my tolerance = NUMfpp -> eps * numberOfRows;
  78. my u = MATzero (numberOfRows, numberOfColumns);
  79. my v = MATzero (numberOfColumns, numberOfColumns);
  80. my d = VECzero (numberOfColumns);
  81. }
  82. autoSVD SVD_create (integer numberOfRows, integer numberOfColumns) {
  83. try {
  84. autoSVD me = Thing_new (SVD);
  85. SVD_init (me.get(), numberOfRows, numberOfColumns);
  86. return me;
  87. } catch (MelderError) {
  88. Melder_throw (U"SVD not created.");
  89. }
  90. }
  91. autoSVD SVD_createFromGeneralMatrix (constMAT m) {
  92. try {
  93. autoSVD me = SVD_create (m.nrow, m.ncol);
  94. for (integer i = 1; i <= my numberOfRows; i ++) {
  95. for (integer j = 1; j <= my numberOfColumns; j ++) {
  96. my u [i] [j] = my isTransposed ? m [j] [i] : m [i] [j];
  97. }
  98. }
  99. SVD_compute (me.get());
  100. return me;
  101. } catch (MelderError) {
  102. Melder_throw (U"SVD not created from general matrix.");
  103. }
  104. }
  105. void SVD_svd_d (SVD me, constMAT m) {
  106. Melder_assert ((my numberOfRows == m.nrow && my numberOfColumns == m.ncol) ||
  107. (my isTransposed && my numberOfRows == m.ncol && my numberOfColumns == m.nrow));
  108. for (integer i = 1; i <= my numberOfRows; i ++) {
  109. for (integer j = 1; j <= my numberOfColumns; j ++) {
  110. my u [i] [j] = my isTransposed ? m [j] [i] : m [i] [j];
  111. }
  112. }
  113. SVD_compute (me);
  114. }
  115. void SVD_setTolerance (SVD me, double tolerance) {
  116. my tolerance = tolerance;
  117. }
  118. double SVD_getTolerance (SVD me) {
  119. return my tolerance;
  120. }
  121. /*
  122. Compute svd(A) = U D Vt.
  123. The svd routine from CLAPACK uses (fortran) column major storage, while C uses row major storage.
  124. To solve the problem above we have to transpose the matrix A, calculate the
  125. solution and transpose the U and Vt matrices of the solution.
  126. However, if we solve the transposed problem svd(A') = V D U', we have less work to do:
  127. We may call the algorithm with reverted row/column dimensions, and we switch the U and V'
  128. output arguments.
  129. The only thing that we have to do afterwards is transposing the (small) V matrix
  130. because the SVD-object has row vectors in v.
  131. The sv's are already sorted.
  132. int NUMlapack_dgesvd (char *jobu, char *jobvt, integer *m, integer *n, double *a, integer *lda,
  133. double *s, double *u, integer *ldu, double *vt, integer *ldvt, double *work,
  134. integer *lwork, integer *info);
  135. */
  136. void SVD_compute (SVD me) {
  137. try {
  138. char jobu = 'S'; // the first min(m,n) columns of U are returned in the array U;
  139. char jobvt = 'O'; // the first min(m,n) rows of V**T are overwritten on the array A;
  140. integer m = my numberOfColumns; // number of rows of input matrix
  141. integer n = my numberOfRows; // number of columns of input matrix
  142. integer lda = m, ldu = m, ldvt = m;
  143. integer info, lwork = -1;
  144. double wt [2];
  145. (void) NUMlapack_dgesvd (& jobu, & jobvt, & m, & n, & my u [1] [1], & lda, my d.begin(), & my v [1] [1], & ldu, nullptr, & ldvt, wt, & lwork, & info);
  146. Melder_require (info == 0, U"SVD could not be precomputed.");
  147. lwork = wt [0];
  148. autoVEC work = VECraw (lwork);
  149. (void) NUMlapack_dgesvd (& jobu, & jobvt, & m, & n, & my u [1] [1], & lda, my d.begin(), & my v [1] [1], & ldu, nullptr, & ldvt, work.begin(), & lwork, & info);
  150. Melder_require (info == 0, U"SVD could not be computed.");
  151. MATtranspose_inplace_mustBeSquare (my v.get());
  152. } catch (MelderError) {
  153. Melder_throw (me, U": SVD could not be computed.");
  154. }
  155. }
  156. // V D^2 V'or V D^-2 V
  157. void SVD_getSquared (SVD me, double **m, bool inverse) {
  158. for (integer i = 1; i <= my numberOfColumns; i ++) {
  159. for (integer j = 1; j <= my numberOfColumns; j ++) {
  160. longdouble val = 0.0;
  161. for (integer k = 1; k <= my numberOfColumns; k ++) {
  162. if (my d [k] > 0.0) {
  163. longdouble dsq = my d [k] * my d [k];
  164. longdouble factor = inverse ? 1.0 / dsq : dsq;
  165. val += my v [i] [k] * my v [j] [k] * factor;
  166. }
  167. }
  168. m [i] [j] = (double) val;
  169. }
  170. }
  171. }
  172. autoVEC SVD_solve (SVD me, constVEC b) {
  173. try {
  174. /*
  175. Solve UDV' x = b.
  176. Solution: x = V D^-1 U' b
  177. */
  178. Melder_assert (my numberOfRows == b.size);
  179. autoVEC t = VECzero (my numberOfColumns);
  180. for (integer j = 1; j <= my numberOfColumns; j ++) {
  181. longdouble tmp = 0.0;
  182. if (my d [j] > 0.0) {
  183. for (integer i = 1; i <= my numberOfRows; i ++) {
  184. tmp += my u [i] [j] * b [i];
  185. }
  186. tmp /= my d [j];
  187. }
  188. t [j] = (double) tmp;
  189. }
  190. autoVEC x = VECmul (my v.get(), t.get());
  191. return x;
  192. } catch (MelderError) {
  193. Melder_throw (me, U": not solved.");
  194. }
  195. }
  196. integer SVD_getMinimumNumberOfSingularValues (SVD me, double fractionOfSumOfSingularValues) {
  197. double sumOfSingularValues = SVD_getSumOfSingularValues (me, 1, my numberOfColumns);
  198. double criterion = sumOfSingularValues * fractionOfSumOfSingularValues;
  199. integer j = 1;
  200. longdouble sum = my d [1];
  201. while (sum < criterion && j < my numberOfColumns) {
  202. sum += my d [++ j];
  203. }
  204. return j;
  205. }
  206. void SVD_sort (SVD me) {
  207. try {
  208. autoSVD thee = Data_copy (me);
  209. autoINTVEC index = NUMindexx (my d.get());
  210. for (integer j = 1; j <= my numberOfColumns; j ++) {
  211. integer from = index [my numberOfColumns - j + 1];
  212. my d [j] = thy d [from];
  213. for (integer i = 1; i <= my numberOfRows; i ++)
  214. my u [i] [j] = thy u [i] [from];
  215. for (integer i = 1; i <= my numberOfColumns; i ++)
  216. my v [i] [j] = thy v [i] [from];
  217. }
  218. } catch (MelderError) {
  219. Melder_throw (me, U": not sorted.");
  220. }
  221. }
  222. double SVD_getConditionNumber (SVD me) {
  223. return my d[my numberOfColumns] > 0.0 ? my d[1] / my d[my numberOfColumns] : undefined;
  224. }
  225. double SVD_getSumOfSingularValuesAsFractionOfTotal (SVD me, integer from, integer to) {
  226. double part = SVD_getSumOfSingularValues (me, from, to);
  227. double total = SVD_getSumOfSingularValues (me, 1, my numberOfColumns);
  228. return part / total;
  229. }
  230. double SVD_getSumOfSingularValues (SVD me, integer from, integer to) {
  231. from = from == 0 ? 1 : from;
  232. to = to == 0 ? my numberOfColumns : to;
  233. Melder_require (from > 0 && from <= to && to <= my numberOfColumns, U"The range should be within [1,", my numberOfColumns, U"].");
  234. return NUMsum (my d.subview (from, to));
  235. }
  236. integer SVD_zeroSmallSingularValues (SVD me, double tolerance) {
  237. if (tolerance == 0.0)
  238. tolerance = my tolerance;
  239. double dmax = my d [1];
  240. for (integer i = 2; i <= my numberOfColumns; i ++)
  241. if (my d [i] > dmax)
  242. dmax = my d [i];
  243. integer numberOfZeroed = 0;
  244. for (integer i = 1; i <= my numberOfColumns; i ++)
  245. if (my d [i] < dmax * tolerance) {
  246. my d [i] = 0.0;
  247. numberOfZeroed ++;
  248. }
  249. return numberOfZeroed;
  250. }
  251. integer SVD_getRank (SVD me) {
  252. integer rank = 0;
  253. for (integer i = 1; i <= my numberOfColumns; i ++)
  254. if (my d [i] > 0.0)
  255. rank ++;
  256. return rank;
  257. }
  258. /*
  259. SVD of A = U D V'.
  260. If u [i] is the i-th column vector of U and v [i] the i-th column vector of V and s [i] the i-th singular value,
  261. we can write the svd expansion A = sum_{i=1}^n {d [i] u [i] v [i]'}.
  262. Golub & van Loan, 3rd ed, p 71.
  263. */
  264. void SVD_synthesize (SVD me, integer sv_from, integer sv_to, double **m) {
  265. try {
  266. if (sv_to == 0)
  267. sv_to = my numberOfColumns;
  268. Melder_require (sv_from > 0 && sv_from <= sv_to && sv_to <= my numberOfColumns, U"Indices must be in range [1, ", my numberOfColumns, U"].");
  269. for (integer i = 1; i <= my numberOfRows; i ++)
  270. for (integer j = 1; j <= my numberOfColumns; j ++)
  271. if (my isTransposed)
  272. m [j] [i] = 0.0;
  273. else
  274. m [i] [j] = 0.0;
  275. for (integer k = sv_from; k <= sv_to; k ++) {
  276. for (integer i = 1; i <= my numberOfRows; i ++)
  277. for (integer j = 1; j <= my numberOfColumns; j ++) {
  278. double value = my d [k] * my u [i] [k] * my v [j] [k];
  279. if (my isTransposed)
  280. m [j] [i] += value;
  281. else
  282. m [i] [j] += value;
  283. }
  284. }
  285. } catch (MelderError) {
  286. Melder_throw (me, U": no synthesis.");
  287. }
  288. }
  289. Thing_implement (GSVD, Daata, 0);
  290. void structGSVD :: v_info () {
  291. MelderInfo_writeLine (U"Number of columns: ", numberOfColumns);
  292. }
  293. autoGSVD GSVD_create (integer numberOfColumns) {
  294. try {
  295. autoGSVD me = Thing_new (GSVD);
  296. my numberOfColumns = numberOfColumns;
  297. my q = MATzero (numberOfColumns, numberOfColumns);
  298. my r = MATzero (numberOfColumns, numberOfColumns);
  299. my d1 = VECzero (numberOfColumns);
  300. my d2 = VECzero (numberOfColumns);
  301. return me;
  302. } catch (MelderError) {
  303. Melder_throw (U"GSVD not created.");
  304. }
  305. }
  306. autoGSVD GSVD_create_d (constMAT m1, constMAT m2) {
  307. try {
  308. integer m = m1.nrow, n = m1.ncol, p = m2.nrow;
  309. integer lwork = std::max (std::max (3 * n, m), p) + n;
  310. // Store the matrices a and b as column major!
  311. autoMAT a = MATtranspose (m1);
  312. autoMAT b = MATtranspose (m2);
  313. autoMAT q = MATraw (n, n);
  314. autoVEC alpha = VECraw (n);
  315. autoVEC beta = VECraw (n);
  316. autoVEC work =VECraw (lwork);
  317. autoINTVEC iwork = INTVECraw (n);
  318. char jobu1 = 'N', jobu2 = 'N', jobq = 'Q';
  319. integer k, l, info;
  320. NUMlapack_dggsvd (& jobu1, & jobu2, & jobq, & m, & n, & p, & k, & l,
  321. & a [1] [1], & m, & b [1] [1], & p, alpha.begin(), beta.begin(), nullptr, & m,
  322. nullptr, & p, & q [1] [1], & n, work.begin(), iwork.begin(), & info);
  323. Melder_require (info == 0, U"dggsvd failed with error = ", info);
  324. integer kl = k + l;
  325. autoGSVD me = GSVD_create (kl);
  326. for (integer i = 1; i <= kl; i ++) {
  327. my d1 [i] = alpha [i];
  328. my d2 [i] = beta [i];
  329. }
  330. // Transpose q
  331. MATtranspose_preallocated (my q.get(), q.get());
  332. // Get R from a(1:k+l,n-k-l+1:n)
  333. double *pr = & a [1] [1];
  334. for (integer i = 1; i <= kl; i ++) {
  335. for (integer j = i; j <= kl; j ++) {
  336. my r [i] [j] = pr [i - 1 + (n - kl + j - 1) * m]; /* from col-major */
  337. }
  338. }
  339. return me;
  340. } catch (MelderError) {
  341. Melder_throw (U"GSVD not created.");
  342. }
  343. }
  344. void GSVD_setTolerance (GSVD me, double tolerance) {
  345. my tolerance = tolerance;
  346. }
  347. double GSVD_getTolerance (GSVD me) {
  348. return my tolerance;
  349. }
  350. /* End of file SVD.c */