Configuration.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /* Configuration.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 20010920 +Configuration_getDilationFactor
  20. djmw 20020315 GPL header
  21. djmw 20030513 applied change in numeric label generation
  22. djmw 20030801 Configuration_drawConcentrationEllipses extra argument
  23. djmw 20040303 Moved containsPrintableCharacter to NUM2.c
  24. djmw 20041026 Removed non-used code.
  25. djmw 20050314 Configuration_draw crashed when rowlabel==nullptr
  26. djmw 20061212 Changed info to Melder_writeLine<x> format.
  27. djmw 20071009 wchar
  28. djmw 20071012 Added: o_CAN_WRITE_AS_ENCODING.h
  29. djmw 20100302 Extra test in Configuration_rotate
  30. djmw 20110304 Thing_new
  31. */
  32. #include <ctype.h>
  33. #include "SVD.h"
  34. #include "PCA.h"
  35. #include "Configuration.h"
  36. #include "Configuration_AffineTransform.h"
  37. #include "TableOfReal_extensions.h"
  38. #include "SSCP.h"
  39. #include "oo_DESTROY.h"
  40. #include "Configuration_def.h"
  41. #include "oo_COPY.h"
  42. #include "Configuration_def.h"
  43. #include "oo_EQUAL.h"
  44. #include "Configuration_def.h"
  45. #include "oo_CAN_WRITE_AS_ENCODING.h"
  46. #include "Configuration_def.h"
  47. #include "oo_WRITE_TEXT.h"
  48. #include "Configuration_def.h"
  49. #include "oo_WRITE_BINARY.h"
  50. #include "Configuration_def.h"
  51. #include "oo_READ_TEXT.h"
  52. #include "Configuration_def.h"
  53. #include "oo_READ_BINARY.h"
  54. #include "Configuration_def.h"
  55. #include "oo_DESCRIPTION.h"
  56. #include "Configuration_def.h"
  57. Thing_implement (Configuration, TableOfReal, 0);
  58. void structConfiguration :: v_info () {
  59. structDaata :: v_info ();
  60. MelderInfo_writeLine (U"Number of points: ", numberOfRows);
  61. MelderInfo_writeLine (U"Number of dimensions: ", numberOfColumns);
  62. MelderInfo_writeLine (U"Metric: ", metric);
  63. }
  64. autoConfiguration Configuration_create (integer numberOfPoints, integer numberOfDimensions) {
  65. try {
  66. autoConfiguration me = Thing_new (Configuration);
  67. TableOfReal_init (me.get(), numberOfPoints, numberOfDimensions);
  68. my w = VECraw (numberOfDimensions);
  69. TableOfReal_setSequentialRowLabels (me.get(), 0, 0, nullptr, 1, 1);
  70. TableOfReal_setSequentialColumnLabels (me.get(), 0, 0, U"dimension ", 1, 1);
  71. my metric = 2;
  72. Configuration_setDefaultWeights (me.get());
  73. Configuration_randomize (me.get());
  74. return me;
  75. } catch (MelderError) {
  76. Melder_throw (U"Configuration not created.");
  77. }
  78. }
  79. void Configuration_setMetric (Configuration me, integer metric) {
  80. my metric = metric;
  81. }
  82. void Configuration_setDefaultWeights (Configuration me) {
  83. for (integer i = 1; i <= my numberOfColumns; i ++) {
  84. my w [i] = 1;
  85. }
  86. }
  87. void Configuration_setSqWeights (Configuration me, const double weight[]) {
  88. for (integer i = 1; i <= my numberOfColumns; i ++) {
  89. my w [i] = sqrt (weight [i]);
  90. }
  91. }
  92. void Configuration_normalize (Configuration me, double sumsq, bool columns) {
  93. TableOfReal_centreColumns (me);
  94. if (columns) {
  95. sumsq = sumsq <= 0.0 ? 1.0 : sqrt (sumsq);
  96. MATnormalizeColumns_inplace (my data.get(), 2.0, sumsq);
  97. } else {
  98. if (sumsq <= 0.0)
  99. sumsq = my numberOfRows;
  100. MATnormalize_inplace (my data.get(), 2.0, sumsq);
  101. }
  102. }
  103. void Configuration_randomize (Configuration me) {
  104. for (integer i = 1; i <= my numberOfRows; i ++)
  105. for (integer j = 1; j <= my numberOfColumns; j ++)
  106. my data [i] [j] = NUMrandomUniform (-1.0, 1.0);
  107. }
  108. void Configuration_rotate (Configuration me, integer dimension1, integer dimension2, double angle_degrees) {
  109. const double f = NUMpi * (2.0 - angle_degrees / 180.0);
  110. const double cosa = cos (f), sina = sin (f);
  111. if (dimension1 == dimension2 || angle_degrees == 0)
  112. return;
  113. if (dimension1 > dimension2)
  114. std::swap (dimension1, dimension2);
  115. if (dimension1 < 1 || dimension2 > my numberOfColumns)
  116. return;
  117. for (integer i = 1; i <= my numberOfRows; i ++) {
  118. double x1 = my data [i] [dimension1];
  119. double x2 = my data [i] [dimension2];
  120. my data [i] [dimension1] = cosa * x1 + sina * x2;
  121. my data [i] [dimension2] = - sina * x1 + cosa * x2;
  122. }
  123. }
  124. void Configuration_invertDimension (Configuration me, int dimension) {
  125. if (dimension < 1 || dimension > my numberOfColumns)
  126. return;
  127. for (integer i = 1; i <= my numberOfRows; i ++)
  128. my data [i] [dimension] = - my data [i] [dimension];
  129. }
  130. static double NUMsquaredVariance (double **a, integer nr, integer nc, bool rawPowers) {
  131. longdouble v4 = 0.0;
  132. for (integer j = 1; j <= nc; j ++) {
  133. longdouble sum4 = 0.0, mean = 0.0;
  134. for (integer i = 1; i <= nr; i ++) {
  135. double sq = a [i] [j] * a [i] [j];
  136. sum4 += sq * sq;
  137. mean += sq;
  138. }
  139. v4 += sum4;
  140. if (! rawPowers) {
  141. v4 -= mean * mean / nr;
  142. }
  143. }
  144. return (double) v4;
  145. }
  146. /*
  147. Varimax rotation, implementation according to:
  148. Jos Ten Berge (1995), "Suppressing permutations or rigid
  149. planar rotations: a remedy against nonoptimal varimax rotations",
  150. Psychometrika 60, 437-446.
  151. */
  152. static void NUMvarimax (double **xm, double **ym, integer nr, integer nc, bool normalizeRows, bool quartimax, integer maximumNumberOfIterations, double tolerance) {
  153. Melder_assert (nr > 0 && nc > 0);
  154. NUMmatrix_copyElements (xm, ym, 1, nr, 1, nc);
  155. if (nc == 1) {
  156. return;
  157. }
  158. if (nc == 2) {
  159. maximumNumberOfIterations = 1;
  160. }
  161. autoNUMvector<double> u (1, nr);
  162. autoNUMvector<double> v (1, nr);
  163. autoNUMvector<double> norm;
  164. // Normalize sum of squares of each row to one.
  165. // After rotation we have to rescale.
  166. if (normalizeRows) {
  167. norm.reset (1, nr);
  168. for (integer i = 1; i <= nr; i ++) {
  169. for (integer j = 1; j <= nc; j ++) {
  170. norm [i] += ym [i] [j] * ym [i] [j];
  171. }
  172. if (norm [i] <= 0.0) {
  173. continue;
  174. }
  175. norm [i] = sqrt (norm [i]);
  176. for (integer j = 1; j <= nc; j ++) {
  177. ym [i] [j] /= norm [i];
  178. }
  179. }
  180. }
  181. // Initial squared "variance".
  182. double varianceSq = NUMsquaredVariance (ym, nr, nc, quartimax);
  183. if (varianceSq == 0.0) {
  184. return;
  185. }
  186. // Treat columns pairwise.
  187. double varianceSq_old;
  188. integer numberOfIterations = 0;
  189. do {
  190. for (integer c1 = 1; c1 <= nc; c1 ++) {
  191. for (integer c2 = c1 + 1; c2 <= nc; c2 ++) {
  192. double um = 0.0, vm = 0.0;
  193. for (integer i = 1; i <= nr; i ++) {
  194. double x = ym [i] [c1];
  195. double y = ym [i] [c2];
  196. u [i] = x * x - y * y;
  197. um += u [i];
  198. v [i] = 2.0 * x * y;
  199. vm += v [i];
  200. }
  201. um /= nr; vm /= nr;
  202. if (quartimax || nr == 1) {
  203. um = vm = 0.0;
  204. }
  205. /*
  206. In the paper just before equation (1):
  207. a = 2n u'v, b = n(u'u-v'v), c = sqrt(a^2+b^2)
  208. w = -sign(a) sqrt((b+c) / 2c)
  209. Tricks: multiplication with n drops out!
  210. a's multiplication by 2 outside the loop.
  211. */
  212. double a = 0.0, b = 0.0;
  213. for (integer i = 1; i <= nr; i ++) {
  214. double ui = u [i] - um;
  215. double vi = v [i] - vm;
  216. a += ui * vi;
  217. b += ui * ui - vi * vi;
  218. }
  219. double c = sqrt (4.0 * a * a + b * b);
  220. double w = sqrt ( (c + b) / (2.0 * c));
  221. if (a > 0.0) {
  222. w = -w;
  223. }
  224. double cost = sqrt (0.5 + 0.5 * w);
  225. double sint = sqrt (0.5 - 0.5 * w);
  226. double t22 = cost;
  227. double t11 = cost;
  228. double t12 = sint;
  229. double t21 = -sint;
  230. // Prevent permutations: when w < 0, i.e., a > 0, swap columns of T:/
  231. if (w < 0.0) {
  232. t11 = sint; t12 = t21 = cost; t22 = -sint;
  233. }
  234. // Rotate in the plane spanned by c1 and c2.
  235. for (integer i = 1; i <= nr; i ++) {
  236. double *xt = ym [i], xtc1 = xt [c1];
  237. xt [c1] = xtc1 * t11 + xt [c2] * t21;
  238. xt [c2] = xtc1 * t12 + xt [c2] * t22;
  239. }
  240. }
  241. }
  242. numberOfIterations++;
  243. varianceSq_old = varianceSq;
  244. varianceSq = NUMsquaredVariance (ym, nr, nc, quartimax);
  245. } while (fabs (varianceSq_old - varianceSq) / varianceSq_old > tolerance &&
  246. numberOfIterations < maximumNumberOfIterations);
  247. if (normalizeRows) {
  248. for (integer i = 1; i <= nr; i ++) {
  249. for (integer j = 1; j <= nc; j ++) {
  250. ym [i] [j] *= norm [i];
  251. }
  252. }
  253. }
  254. }
  255. autoConfiguration Configuration_varimax (Configuration me, bool normalizeRows, bool quartimax, integer maximumNumberOfIterations, double tolerance) {
  256. try {
  257. autoConfiguration thee = Data_copy (me);
  258. NUMvarimax (my data.at, thy data.at, my numberOfRows, my numberOfColumns, normalizeRows, quartimax, maximumNumberOfIterations, tolerance);
  259. return thee;
  260. } catch (MelderError) {
  261. Melder_throw (me, U": varimax rotation not performed.");
  262. }
  263. }
  264. autoConfiguration Configuration_congruenceRotation (Configuration me, Configuration thee, integer maximumNumberOfIterations, double tolerance) {
  265. try {
  266. autoAffineTransform at = Configurations_to_AffineTransform_congruence (me, thee, maximumNumberOfIterations, tolerance);
  267. autoConfiguration him = Configuration_AffineTransform_to_Configuration (me, at.get());
  268. return him;
  269. } catch (MelderError) {
  270. Melder_throw (me, U": congruence rotation not performed.");
  271. }
  272. }
  273. /* Replace by TableOfReal_to_Configuration_pca ??? */
  274. void Configuration_rotateToPrincipalDirections (Configuration me) {
  275. try {
  276. autoMAT newData = matrixcopy (my data.get());
  277. NUMdmatrix_into_principalComponents (my data.at, my numberOfRows, my numberOfColumns, my numberOfColumns, newData.at);
  278. my data = newData.move();
  279. } catch (MelderError) {
  280. Melder_throw (me, U": not rotated to principal directions.");
  281. }
  282. }
  283. void Configuration_draw (Configuration me, Graphics g, int xCoordinate, int yCoordinate, double xmin, double xmax, double ymin, double ymax, int labelSize, bool useRowLabels, conststring32 label, bool garnish)
  284. {
  285. integer nPoints = my numberOfRows, numberOfDimensions = my numberOfColumns;
  286. if (numberOfDimensions > 1 && (xCoordinate > numberOfDimensions || yCoordinate > numberOfDimensions))
  287. return;
  288. if (numberOfDimensions == 1)
  289. xCoordinate = 1;
  290. int fontSize = Graphics_inqFontSize (g), noLabel = 0;
  291. if (labelSize == 0)
  292. labelSize = fontSize;
  293. autoNUMvector<double> x (1, nPoints);
  294. autoNUMvector<double> y (1, nPoints);
  295. for (integer i = 1; i <= nPoints; i ++) {
  296. x [i] = my data [i] [xCoordinate] * my w [xCoordinate];
  297. y [i] = numberOfDimensions > 1 ? my data [i] [yCoordinate] * my w [yCoordinate] : 0.0;
  298. }
  299. if (xmax <= xmin) {
  300. NUMvector_extrema (x.peek(), 1, nPoints, &xmin, &xmax);
  301. }
  302. if (xmax <= xmin) {
  303. xmax += 1.0;
  304. xmin -= 1.0;
  305. }
  306. if (ymax <= ymin) {
  307. NUMvector_extrema (y.peek(), 1, nPoints, &ymin, &ymax);
  308. }
  309. if (ymax <= ymin) {
  310. ymax += 1.0;
  311. ymin -= 1.0;
  312. }
  313. Graphics_setWindow (g, xmin, xmax, ymin, ymax);
  314. Graphics_setInner (g);
  315. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
  316. Graphics_setFontSize (g, labelSize);
  317. for (integer i = 1; i <= my numberOfRows; i ++) {
  318. if (x [i] >= xmin && x [i] <= xmax && y [i] >= ymin && y [i] <= ymax) {
  319. conststring32 plotLabel = ( useRowLabels ? my rowLabels [i].get() : label );
  320. if (Melder_findInk (plotLabel)) {
  321. Graphics_text (g, x [i], y [i], plotLabel);
  322. } else {
  323. noLabel ++;
  324. }
  325. }
  326. }
  327. Graphics_setFontSize (g, fontSize);
  328. Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_BOTTOM);
  329. Graphics_unsetInner (g);
  330. if (garnish) {
  331. Graphics_drawInnerBox (g);
  332. Graphics_marksBottom (g, 2, true, true, false);
  333. if (numberOfDimensions > 1) {
  334. Graphics_marksLeft (g, 2, true, true, false);
  335. if (my columnLabels [xCoordinate]) {
  336. Graphics_textBottom (g, true, my columnLabels [xCoordinate].get());
  337. }
  338. if (my columnLabels [yCoordinate]) {
  339. Graphics_textLeft (g, true, my columnLabels [yCoordinate].get());
  340. }
  341. }
  342. }
  343. if (noLabel > 0) {
  344. Melder_warning (U"Configuration_draw: ", noLabel, U" from ", my numberOfRows, U" labels are not visible because they are empty or they contain only spaces or they contain only non-printable characters");
  345. }
  346. }
  347. void Configuration_drawConcentrationEllipses (Configuration me, Graphics g, double scale, bool confidence, conststring32 label, integer d1, integer d2, double xmin, double xmax, double ymin, double ymax, int fontSize, bool garnish) {
  348. autoSSCPList sscps = TableOfReal_to_SSCPList_byLabel (me);
  349. SSCPList_drawConcentrationEllipses (sscps.get(), g, scale, confidence, label, d1, d2, xmin, xmax, ymin, ymax, fontSize, garnish);
  350. }
  351. autoConfiguration TableOfReal_to_Configuration (TableOfReal me) {
  352. try {
  353. autoConfiguration thee = Configuration_create (my numberOfRows, my numberOfColumns);
  354. matrixcopy_preallocated (thy data.get(), my data.get());
  355. TableOfReal_copyLabels (me, thee.get(), 1, 1);
  356. return thee;
  357. } catch (MelderError) {
  358. Melder_throw (me, U": not converted.");
  359. }
  360. }
  361. autoConfiguration TableOfReal_to_Configuration_pca (TableOfReal me, integer numberOfDimensions) {
  362. try {
  363. if (numberOfDimensions < 1 || numberOfDimensions > my numberOfColumns)
  364. numberOfDimensions = my numberOfColumns;
  365. autoPCA pca = TableOfReal_to_PCA_byRows (me);
  366. autoConfiguration thee = PCA_TableOfReal_to_Configuration (pca.get(), me, numberOfDimensions);
  367. return thee;
  368. } catch (MelderError) {
  369. Melder_throw (me, U": pca not performed.");
  370. }
  371. }
  372. /********************** Examples *********************************************/
  373. autoConfiguration Configuration_createLetterRExample (int choice) {
  374. double x1[33] = { 0,
  375. -5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
  376. -5, -4, -3, -2, -1, 0, 1, 2.25, 3, 3,
  377. 2.25, 1, 0, -1, -2, -3, -4, -1, 0, 1, 2, 3 };
  378. double y1[33] = { 0,
  379. -6, -5, -4, -3, -2, -1, 0, 1, 2, 3,
  380. 4, 4, 4, 4, 4, 4, 4, 3.5, 2, 1,
  381. -0.5, -1, -1, -1, -1, -1, -1, -2, -3, -4, -5, -6 };
  382. double x2[33] = {0, 0.94756043346272423, 0.73504466902509913,
  383. 0.4528453515175927, 0.46311499024105723, 0.30345454816993439,
  384. 0.075184942115601547, -0.090010071904764719, -0.19630977381424003,
  385. -0.36341509807865086, -0.54216996409132612, -0.68704678013309872,
  386. -0.67370169194623086, -0.69336494336440502, -0.67809065144478664,
  387. -0.61382610572366281, -0.68656530656078996, -0.57704879646736551,
  388. -0.63417502349009069, -0.37153350651419026, -0.091809666009009777,
  389. 0.054833807442559397, 0.1445593164362155, 0.055587230806920782,
  390. 0.18201798315035453, 0.048445620192953162, 0.081595930742961439,
  391. 0.20063623749033621, 0.28546520751183313, 0.39384438699721991,
  392. 0.62832258520372286, 0.78548335015622228, 1.0610707888793069 };
  393. double y2[33] = {0, 0.49630791172076621, 0.53320347382055022,
  394. 0.62384637225470441, 0.47592708487655661, 0.50364353255684202,
  395. 0.55311720162084443, 0.55118713773007066, 0.50007736370068601,
  396. 0.40432332354648709, 0.49817059660482677, 0.49803436631629411,
  397. 0.33213829258059019, 0.14585700576425648, -0.022110500334692869,
  398. -0.1752555003289698, -0.29448744336706828, -0.45639468287493545,
  399. -0.59177815505008013, -0.74980550818568981, -0.78095916436791279,
  400. -0.64447562732895125, -0.49526830813007033, -0.22443396573313243,
  401. -0.066378148077667398, -0.03498490725857361, 0.16196028200653381,
  402. 0.30633527000982519, -0.14894460651161745, -0.30808798640907431,
  403. -0.35920781945385832, -0.62766325578928184, -0.60389363590825562 };
  404. try {
  405. double *x, *y;
  406. autoConfiguration me = Configuration_create (32, 2);
  407. if (choice == 2) {
  408. x = x2; y = y2;
  409. Thing_setName (me.get(), U"R_fit");
  410. } else {
  411. x = x1; y = y1;
  412. Thing_setName (me.get(), U"R");
  413. }
  414. for (integer i = 1; i <= 32; i ++) {
  415. char32 s [20];
  416. Melder_sprint (s, 20, i);
  417. TableOfReal_setRowLabel (me.get(), i, s);
  418. my data [i] [1] = x [i];
  419. my data [i] [2] = y [i];
  420. }
  421. return me;
  422. } catch (MelderError) {
  423. Melder_throw (U"Letter R Configuration not created.");
  424. }
  425. }
  426. autoConfiguration Configuration_createCarrollWishExample () {
  427. double x [10] = {0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0 };
  428. double y [10] = {0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0 };
  429. char32 const *label[] = { U"", U"A", U"B", U"C", U"D", U"E", U"F", U"G", U"H", U"I"};
  430. try {
  431. integer nObjects = 9;
  432. autoConfiguration me = Configuration_create (nObjects, 2);
  433. for (integer i = 1; i <= nObjects; i ++) {
  434. my data [i] [1] = x [i];
  435. my data [i] [2] = y [i];
  436. TableOfReal_setRowLabel (me.get(), i, label[i]);
  437. }
  438. return me;
  439. } catch (MelderError) {
  440. Melder_throw (U"Carroll Wish Configuration not created.");
  441. }
  442. }
  443. /************ CONFIGURATIONS **************************************/
  444. Thing_implement (ConfigurationList, TableOfRealList, 0);
  445. /* End of file Configuration.cpp */