Configuration_AffineTransform.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /* Configuration_AffineTransform.cpp
  2. *
  3. * Copyright (C) 1993-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 20020315 GPL header
  20. */
  21. #include "Configuration_AffineTransform.h"
  22. #include "Configuration_and_Procrustes.h"
  23. #include "Procrustes.h"
  24. #include "SVD.h"
  25. #undef your
  26. #define your ((AffineTransform_Table) thy methods) ->
  27. static void do_steps45 (constMAT w, MAT t, constMAT c, double *f) {
  28. // Step 4 || 10: If W'T has negative diagonal elements, multiply corresponding columns in T by -1.
  29. for (integer i = 1; i <= w.ncol; i ++) {
  30. double d = 0.0;
  31. for (integer k = 1; k <= w.ncol; k++) {
  32. d += w [k] [i] * t [k] [i];
  33. }
  34. if (d < 0.0) {
  35. for (integer k = 1; k <= w.ncol; k++) {
  36. t [k] [i] = -t [k] [i];
  37. }
  38. }
  39. }
  40. // Step 5 & 11: f = tr W'T (Diag (T'CT))^-1/2
  41. *f = 0.0;
  42. for (integer i = 1; i <= w.ncol; i ++) {
  43. longdouble d = 0.0, tct = 0.0;
  44. for (integer k = 1; k <= w.ncol; k ++) {
  45. d += w [k] [i] * t [k] [i];
  46. for (integer j = 1; j <= w.ncol; j ++) {
  47. tct += t [k] [i] * c [k] [j] * t [j] [i];
  48. }
  49. }
  50. if (tct > 0.0) {
  51. *f += d / sqrt (tct);
  52. }
  53. }
  54. }
  55. /*
  56. Using: Kiers & Groenen (1996), A monotonically convergent congruence algorithm for orthogona congruence rotation,
  57. Psychometrika (61), 375-389.
  58. */
  59. void NUMmaximizeCongruence_inplace (MAT t, constMAT b, constMAT a, integer maximumNumberOfIterations, double tolerance) {
  60. Melder_assert (t.ncol == b.ncol && b.nrow == a.nrow && b.ncol == a.ncol);
  61. integer numberOfIterations = 0;
  62. if (b.ncol == 1) {
  63. t [1] [1] = 1.0;
  64. return;
  65. }
  66. integer nr = b.nrow, nc = b.ncol;
  67. autoMAT u = MATzero (nc, nc);
  68. autoVEC evec = VECzero (nc);
  69. autoSVD svd = SVD_create (nc, nc);
  70. // Steps 1 & 2: C = A'A and W = A'B
  71. autoMAT c = MATmul_tn (a, a);
  72. autoMAT w = MATmul_tn (a, b);
  73. double checkc = NUMsum (c.get());
  74. double checkw = NUMsum (w.get());
  75. Melder_require (checkc != 0.0 && checkw != 0.0, U"NUMmaximizeCongruence: the matrix should not be zero.");
  76. // Scale W by (diag(B'B))^-1/2
  77. for (integer j = 1; j <= nc; j ++) {
  78. longdouble scale = 0.0;
  79. for (integer k = 1; k <= nr; k ++) {
  80. scale += b [k] [j] * b [k] [j];
  81. }
  82. if (scale > 0.0) {
  83. scale = 1.0 / sqrt (scale);
  84. }
  85. for (integer i = 1; i <= nc; i++) {
  86. w [i] [j] *= scale;
  87. }
  88. }
  89. // Step 3: largest eigenvalue of C
  90. evec [1] = 1.0;
  91. double rho, f, f_old;
  92. NUMdominantEigenvector (c.get(), evec.get(), & rho, 1.0e-6);
  93. do_steps45 (w.get(), t, c.get(), & f);
  94. do {
  95. for (integer j = 1; j <= nc; j ++) {
  96. // Step 7.a
  97. longdouble p = 0.0;
  98. for (integer k = 1; k <= nc; k ++) {
  99. for (integer i = 1; i <= nc; i ++) {
  100. p += t [k] [j] * c [k] [i] * t [i] [j];
  101. }
  102. }
  103. // Step 7.b
  104. longdouble q = 0.0;
  105. for (integer k = 1; k <= nc; k ++) {
  106. q += w [k] [j] * t [k] [j];
  107. }
  108. // Step 7.c
  109. if (q == 0.0) {
  110. for (integer i = 1; i <= nc; i ++) {
  111. u [i] [j] = 0.0;
  112. }
  113. } else {
  114. longdouble ww = 0.0;
  115. for (integer k = 1; k <= nc; k ++) {
  116. ww += w [k] [j] * w [k] [j];
  117. }
  118. for (integer i = 1; i <= nc; i ++) {
  119. longdouble ct = 0.0;
  120. for (integer k = 1; k <= nc; k ++) {
  121. ct += c [i] [k] * t [k] [j];
  122. }
  123. u [i] [j] = (q * (ct - rho * t [i] [j]) / p - 2.0 * ww * t [i] [j] / q - w [i] [j]) / sqrt (p);
  124. }
  125. }
  126. }
  127. // Step 8
  128. SVD_svd_d (svd.get(), u.get());
  129. // Step 9
  130. for (integer i = 1; i <= nc; i ++) {
  131. for (integer j = 1; j <= nc; j ++) {
  132. t [i] [j] = 0.0;
  133. for (integer k = 1; k <= nc; k ++) {
  134. t [i] [j] -= svd -> u [i] [k] * svd -> v [j] [k];
  135. }
  136. }
  137. }
  138. numberOfIterations++;
  139. f_old = f;
  140. // Steps 10 & 11 equal steps 4 & 5
  141. do_steps45 (w.get(), t, c.get(), & f);
  142. } while (fabs (f_old - f) / f_old > tolerance && numberOfIterations < maximumNumberOfIterations);
  143. }
  144. autoAffineTransform Configurations_to_AffineTransform_congruence (Configuration me, Configuration thee, integer maximumNumberOfIterations, double tolerance) {
  145. try {
  146. // Use Procrustes transform to obtain starting configuration.
  147. // (We only need the transformation matrix T.)
  148. autoProcrustes p = Configurations_to_Procrustes (me, thee, false);
  149. Melder_assert (p -> dimension == my data.ncol);
  150. Melder_assert (p -> dimension == thy data.ncol);
  151. NUMmaximizeCongruence_inplace (p -> r.get(), my data.get(), thy data.get(), maximumNumberOfIterations, tolerance);
  152. autoAffineTransform at = AffineTransform_create (p -> dimension);
  153. matrixcopy_preallocated (at -> r.get(), p -> r.get());
  154. return at;
  155. } catch (MelderError) {
  156. Melder_throw (me, U": no congruence transformation created.");
  157. }
  158. }
  159. autoConfiguration Configuration_AffineTransform_to_Configuration (Configuration me, AffineTransform thee) {
  160. try {
  161. Melder_require (my numberOfColumns == thy dimension, U"The number of columns in the Configuration should equal the dimension of the transform.");
  162. autoConfiguration him = Data_copy (me);
  163. // Apply transformation YT
  164. thy v_transform (my data.at, my numberOfRows, his data.at);
  165. return him;
  166. } catch (MelderError) {
  167. Melder_throw (U"Configuration not created.");
  168. }
  169. }
  170. /* End of file Configuration_AffineTransform.cpp */