MAT_numerics.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /* MAT_numerics.cpp
  2. *
  3. * Copyright (C) 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.
  13. * See the GNU 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. #include "NUMclapack.h"
  19. #include "MAT_numerics.h"
  20. void MAT_getEigenSystemFromSymmetricMatrix_inplace (MAT a, bool wantEigenvectors, VEC eigenvalues, bool sortAscending) {
  21. Melder_assert (a.nrow == a.ncol);
  22. Melder_assert (eigenvalues.size == a.ncol);
  23. char jobz = wantEigenvectors ? 'V' : 'N', uplo = 'U';
  24. integer workSize = -1, n = a.ncol, info;
  25. double wt [1], tmp;
  26. // 0. No need to transpose a because it is a symmetric matrix
  27. // 1. Query for the size of the work array
  28. (void) NUMlapack_dsyev (& jobz, & uplo, & n, & a [1] [1], & n, eigenvalues.begin(), wt, & workSize, & info);
  29. Melder_require (info == 0, U"dsyev initialization code = ", info, U").");
  30. workSize = Melder_iceiling (wt [0]);
  31. autoVEC work = VECraw (workSize);
  32. // 2. Calculate the eigenvalues and eigenvectors (row-wise)
  33. (void) NUMlapack_dsyev (& jobz, & uplo, & n, & a [1] [1], & n, eigenvalues.begin(), work.begin(), & workSize, & info);
  34. Melder_require (info == 0, U"dsyev code = ", info, U").");
  35. // 3. Eigenvalues are returned in ascending order
  36. if (! sortAscending) {
  37. for (integer i = 1; i <= n / 2; i ++) {
  38. integer ilast = n - i + 1;
  39. std::swap (eigenvalues [i], eigenvalues [ilast]);
  40. if (wantEigenvectors) {
  41. for (integer j = 1; j <= n; j ++)
  42. std::swap (a [i] [j], a [ilast] [j]);
  43. }
  44. }
  45. }
  46. }
  47. void MAT_getEigenSystemFromSymmetricMatrix (constMAT a, autoMAT *out_eigenvectors, autoVEC *out_eigenvalues, bool sortAscending) {
  48. Melder_assert (a.nrow == a.ncol);
  49. autoVEC eigenvalues = VECraw (a.nrow);
  50. autoMAT eigenvectors = MATtranspose (a);
  51. bool wantEigenvectors = out_eigenvectors != nullptr;
  52. MAT_getEigenSystemFromSymmetricMatrix_inplace (eigenvectors.get(), wantEigenvectors, eigenvalues.get(), sortAscending);
  53. if (out_eigenvectors) *out_eigenvectors = eigenvectors.move ();
  54. if (out_eigenvalues) *out_eigenvalues = eigenvalues.move ();
  55. }
  56. void MAT_eigenvectors_decompress (constMAT eigenvectors, constVEC eigenvalues_re, constVEC eigenvalues_im, autoMAT *out_eigenvectors_reim) {
  57. integer n = eigenvalues_re.size;
  58. Melder_assert (eigenvalues_im.size == n);
  59. Melder_assert (eigenvectors.nrow == n && eigenvectors.ncol == eigenvectors.nrow);
  60. autoMAT eigenvectors_reim = MATzero (n, 2 * n);
  61. integer pair_index = 0;
  62. for (integer ivec = 1; ivec <= eigenvalues_re.size; ivec ++) {
  63. // eigenvalues of a real matrix are either real or occur in complex conjugate pairs
  64. if (eigenvalues_im [ivec] == 0.0) { // real eigenvalue
  65. for (integer j = 1; j <= n; j ++)
  66. eigenvectors_reim [j] [2 * ivec - 1] = eigenvectors [ivec] [j];
  67. } else if (ivec > 1 && eigenvalues_re [ivec] == eigenvalues_re [ivec - 1] &&
  68. eigenvalues_im [ivec] == -eigenvalues_im [ivec - 1] && ivec - pair_index > 1) {
  69. for (integer j = 1; j <= n; j ++) {
  70. eigenvectors_reim [j] [2 * (ivec - 1) - 1]= eigenvectors [ivec - 1] [j];
  71. eigenvectors_reim [j] [2 * (ivec - 1)] = eigenvectors [ivec] [j];
  72. eigenvectors_reim [j] [2 * ivec - 1] = eigenvectors [ivec - 1] [j];
  73. eigenvectors_reim [j] [2 * ivec] = eigenvectors [ivec] [j] != 0.0 ? -eigenvectors [ivec] [j] : 0.0; // avoid -0
  74. }
  75. pair_index = ivec; // guard for multiple equal complex conjugate pairs
  76. }
  77. }
  78. if (out_eigenvectors_reim) *out_eigenvectors_reim = eigenvectors_reim.move();
  79. }
  80. void MAT_getEigenSystemFromGeneralMatrix (constMAT a, autoMAT *out_lefteigenvectors, autoMAT *out_righteigenvectors, autoVEC *out_eigenvalues_re, autoVEC *out_eigenvalues_im) {
  81. Melder_assert (a.nrow == a.ncol);
  82. integer n = a.nrow, info, workSize = -1;
  83. char jobvl = out_lefteigenvectors ? 'V' : 'N';
  84. integer left_nvecs = out_lefteigenvectors ? n : 1; // 1x1 if not wanted
  85. char jobvr = out_righteigenvectors ? 'V' : 'N';
  86. integer right_nvecs = out_righteigenvectors ? n : 1;
  87. double wt;
  88. autoMAT data = MATraw (n, n);
  89. MATtranspose_preallocated (data.get(), a); // lapack is fortran storage
  90. autoVEC eigenvalues_re = VECraw (n);
  91. autoVEC eigenvalues_im = VECraw (n);
  92. autoMAT righteigenvectors = MATraw (right_nvecs, right_nvecs); // 1x1 if not needed
  93. autoMAT lefteigenvectors = MATraw (left_nvecs, left_nvecs); // 1x1 if not needed
  94. (void) NUMlapack_dgeev (& jobvl, & jobvr, & n, & data [1] [1], & n, eigenvalues_re.begin(), eigenvalues_im.begin(), & lefteigenvectors [1] [1],
  95. & n, & righteigenvectors [1] [1], & n, & wt, & workSize, & info);
  96. Melder_require (info == 0, U"dgeev initialization code = ", info, U").");
  97. workSize = Melder_iceiling (wt);
  98. autoVEC work = VECraw (workSize);
  99. (void) NUMlapack_dgeev (& jobvl, & jobvr, & n, & data [1] [1], & n, eigenvalues_re.begin(), eigenvalues_im.begin(), & lefteigenvectors [1] [1],
  100. & n, & righteigenvectors [1] [1], & n, & work [1], & workSize, & info);
  101. Melder_require (info == 0, U"dgeev code = ", info, U").");
  102. if (out_righteigenvectors) *out_righteigenvectors = righteigenvectors.move();
  103. if (out_lefteigenvectors) *out_lefteigenvectors = lefteigenvectors.move();
  104. if (out_eigenvalues_re) *out_eigenvalues_re = eigenvalues_re.move();
  105. if (out_eigenvalues_im) *out_eigenvalues_im = eigenvalues_im.move();
  106. }
  107. void MAT_getPrincipalComponentsOfSymmetricMatrix_preallocated (MAT pc, constMAT a, integer nComponents) {
  108. Melder_assert (a.nrow == a.ncol);
  109. Melder_assert (nComponents > 0 && nComponents <= a.ncol);
  110. Melder_assert (pc.nrow == a.nrow && pc.ncol == nComponents);
  111. autoMAT eigenvectors = MATraw (a.nrow, a.nrow);
  112. MAT_getEigenSystemFromSymmetricMatrix (a, & eigenvectors, nullptr, false);
  113. //MATmul_tn_preallocated (pc, a, eigenvectors.verticalBand (1, nComponents)); // TODO
  114. for (integer i = 1; i <= a.nrow; i ++) {
  115. for (integer j = 1; j <= nComponents; j ++) {
  116. longdouble s = 0.0;
  117. for (integer k = 1; k <= a.nrow; k ++)
  118. s += a [k] [i] * eigenvectors [k] [j];
  119. pc [i] [j] = (double) s;
  120. }
  121. }
  122. }
  123. /* End of file MAT_numerics.cpp */