tSneAlgo.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. #include "pch.h"
  2. #include "tSneAlgo.h"
  3. #include <cfloat>
  4. #include <cmath>
  5. #include <cstdlib>
  6. #include <cstdio>
  7. #include <cstring>
  8. #include <ctime>
  9. //This product includes software developed by the Delft University of Technology.
  10. #include "src/t_sne/tsne.h"
  11. #include "src/t_sne/vptree.h"
  12. #include "src/t_sne/sptree.h"
  13. #pragma warning(disable:4996)
  14. static double sign(double inputArrayX) { return (inputArrayX == .0 ? .0 : (inputArrayX < .0 ? -1.0 : 1.0)); }
  15. static void zeroMean(double* inputArrayX, int N, int D);
  16. static void computeGaussianPerplexity(double* inputArrayX, int N, int D, double* P, double perplexity);
  17. static void computeGaussianPerplexity(double* inputArrayX, int N, int D, unsigned int** _row_P, unsigned int** _col_P, double** _val_P, double perplexity, int K);
  18. static double randn();
  19. static void computeExactGradient(double* P, double* Y, int N, int D, double* dC);
  20. static void computeGradient(unsigned int* inp_row_P, unsigned int* inp_col_P, double* inp_val_P, double* Y, int N, int D, double* dC, double theta);
  21. static double evaluateError(double* P, double* Y, int N, int D);
  22. static double evaluateError(unsigned int* row_P, unsigned int* col_P, double* val_P, double* Y, int N, int D, double theta);
  23. static void computeSquaredEuclideanDistance(double* inputArrayX, int N, int D, double* DD);
  24. static void symmetrizeMatrix(unsigned int** row_P, unsigned int** col_P, double** val_P, int N);
  25. tSneAlgo::tSneAlgo(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end, double** YnotInitialized, double perplexity, double learningRate, int maxIter)
  26. :perplexity(perplexity), learningRate(learningRate), N(std::distance(begin, end)), D(begin->bitVec.size()), maxIter(maxIter)
  27. {
  28. //N -> amount of dataPoints
  29. //D -> Dimension of DataPoints
  30. qDebug() << "N:" << N << " D:" << D;
  31. //Create Input Matrix
  32. inputArrayX = new double[N * D];
  33. for (int n = 0; n < N; n++) {
  34. const SolutionPointData& sol = *std::next(begin, n);
  35. for (int d = 0; d < D; d++) {
  36. inputArrayX[n * D + d] = sol.bitVec[d] ? 1.0 : 0.0;
  37. }
  38. }
  39. //Create Output Matrix
  40. *YnotInitialized = outputArrayY = (double*)calloc(N * outputDimesion, sizeof(double));
  41. }
  42. tSneAlgo::~tSneAlgo()
  43. {
  44. reset();
  45. delete inputArrayX;
  46. delete outputArrayY;
  47. }
  48. void tSneAlgo::run()
  49. {
  50. //TSNE::run
  51. //Init
  52. // Set random seed
  53. if (useRandomSeed != true) {
  54. if (randomSeet >= 0) {
  55. printf("Using random seed: %d\n", randomSeet);
  56. srand((unsigned int)randomSeet);
  57. }
  58. else {
  59. printf("Using current time as random seed...\n");
  60. srand(time(NULL));
  61. }
  62. }
  63. // Determine whether we are using an exact algorithm
  64. if (N - 1 < 3 * perplexity) { printf("Perplexity too large for the number of data points!\n"); exit(1); }
  65. printf("Using no_dims = %d, perplexity = %f, and theta = %f\n", outputDimesion, perplexity, theta);
  66. bool exact = (theta == .0) ? true : false;
  67. // Set learning parameters
  68. float total_time = .0;
  69. clock_t start, end;
  70. double momentum = .5, final_momentum = .8;
  71. // Allocate some memory
  72. double* dY = (double*)malloc(N * outputDimesion * sizeof(double));
  73. double* uY = (double*)malloc(N * outputDimesion * sizeof(double));
  74. double* gains = (double*)malloc(N * outputDimesion * sizeof(double));
  75. if (dY == NULL || uY == NULL || gains == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  76. for (int i = 0; i < N * outputDimesion; i++) uY[i] = .0;
  77. for (int i = 0; i < N * outputDimesion; i++) gains[i] = 1.0;
  78. // Normalize input data (to prevent numerical problems)
  79. printf("Computing input similarities...\n");
  80. start = clock();
  81. zeroMean(inputArrayX, N, D);
  82. double max_X = 0.0;
  83. for (int i = 0; i < N * D; i++) {
  84. if (fabs(inputArrayX[i]) > max_X) max_X = fabs(inputArrayX[i]);
  85. }
  86. for (int i = 0; i < N * D; i++) inputArrayX[i] /= max_X;
  87. // Compute input similarities for exact t-SNE
  88. double* P = nullptr; unsigned int* row_P = nullptr; unsigned int* col_P = nullptr; double* val_P = nullptr;
  89. if (exact) {
  90. // Compute similarities
  91. printf("Exact?");
  92. P = (double*)malloc(N * N * sizeof(double));
  93. if (P == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  94. computeGaussianPerplexity(inputArrayX, N, D, P, perplexity);
  95. // Symmetrize input similarities
  96. printf("Symmetrizing...\n");
  97. int nN = 0;
  98. for (int n = 0; n < N; n++) {
  99. int mN = (n + 1) * N;
  100. for (int m = n + 1; m < N; m++) {
  101. P[nN + m] += P[mN + n];
  102. P[mN + n] = P[nN + m];
  103. mN += N;
  104. }
  105. nN += N;
  106. }
  107. double sum_P = .0;
  108. for (int i = 0; i < N * N; i++) sum_P += P[i];
  109. for (int i = 0; i < N * N; i++) P[i] /= sum_P;
  110. }
  111. // Compute input similarities for approximate t-SNE
  112. else {
  113. // Compute asymmetric pairwise input similarities
  114. computeGaussianPerplexity(inputArrayX, N, D, &row_P, &col_P, &val_P, perplexity, (int)(3 * perplexity));
  115. // Symmetrize input similarities
  116. symmetrizeMatrix(&row_P, &col_P, &val_P, N);
  117. double sum_P = .0;
  118. for (int i = 0; i < row_P[N]; i++) sum_P += val_P[i];
  119. for (int i = 0; i < row_P[N]; i++) val_P[i] /= sum_P;
  120. }
  121. end = clock();
  122. // Lie about the P-values
  123. if (exact) { for (int i = 0; i < N * N; i++) P[i] *= 12.0; }
  124. else { for (int i = 0; i < row_P[N]; i++) val_P[i] *= 12.0; }
  125. // Initialize solution (randomly)
  126. if (skipRandomInit != true) {
  127. for (int i = 0; i < N * outputDimesion; i++) outputArrayY[i] = randn() * .0001;
  128. }
  129. // Perform main training loop
  130. if (exact) printf("Input similarities computed in %4.2f seconds!\nLearning embedding...\n", (float)(end - start) / CLOCKS_PER_SEC);
  131. else printf("Input similarities computed in %4.2f seconds (sparsity = %f)!\nLearning embedding...\n", (float)(end - start) / CLOCKS_PER_SEC, (double)row_P[N] / ((double)N * (double)N));
  132. start = clock();
  133. double last_C = -1;
  134. for (actualIteration = 0; actualIteration < maxIter; actualIteration++) {
  135. checkPaused();
  136. if (checkCancel()) break;
  137. emit changedIter(actualIteration);
  138. // Compute (approximate) gradient
  139. if (exact) computeExactGradient(P, outputArrayY, N, outputDimesion, dY);
  140. else computeGradient(row_P, col_P, val_P, outputArrayY, N, outputDimesion, dY, theta);
  141. // Update gains
  142. for (int i = 0; i < N * outputDimesion; i++) gains[i] = (sign(dY[i]) != sign(uY[i])) ? (gains[i] + .2) : (gains[i] * .8);
  143. for (int i = 0; i < N * outputDimesion; i++) if (gains[i] < .01) gains[i] = .01;
  144. // Perform gradient update (with momentum and gains)
  145. for (int i = 0; i < N * outputDimesion; i++) uY[i] = momentum * uY[i] - learningRate * gains[i] * dY[i];
  146. for (int i = 0; i < N * outputDimesion; i++) outputArrayY[i] += uY[i];
  147. // Make solution zero-mean
  148. zeroMean(outputArrayY, N, outputDimesion);
  149. // Stop lying about the P-values after a while, and switch momentum
  150. if (actualIteration == stopLyingIter) {
  151. if (exact) { for (int i = 0; i < N * N; i++) P[i] /= 12.0; }
  152. else { for (int i = 0; i < row_P[N]; i++) val_P[i] /= 12.0; }
  153. }
  154. if (actualIteration == momentumSwitchIter) momentum = final_momentum;
  155. // Print out progress
  156. if (actualIteration > 0 && (actualIteration % 50 == 0 || actualIteration == maxIter - 1)) {
  157. end = clock();
  158. double C = .0;
  159. if (exact) C = evaluateError(P, outputArrayY, N, outputDimesion);
  160. else C = evaluateError(row_P, col_P, val_P, outputArrayY, N, outputDimesion, theta); // doing approximate computation here!
  161. if (actualIteration == 0)
  162. printf("Iteration %d: error is %f\n", actualIteration + 1, C);
  163. else {
  164. total_time += (float)(end - start) / CLOCKS_PER_SEC;
  165. printf("Iteration %d: error is %f (50 iterations in %4.2f seconds)\n", actualIteration, C, (float)(end - start) / CLOCKS_PER_SEC);
  166. }
  167. start = clock();
  168. last_C = C;
  169. }
  170. }
  171. end = clock(); total_time += (float)(end - start) / CLOCKS_PER_SEC;
  172. // Clean up memory
  173. free(dY);
  174. free(uY);
  175. free(gains);
  176. if (exact) free(P);
  177. else {
  178. free(row_P); row_P = NULL;
  179. free(col_P); col_P = NULL;
  180. free(val_P); val_P = NULL;
  181. }
  182. printf("Fitting performed in %4.2f seconds.\n", total_time);
  183. emit algoDone();
  184. }
  185. void tSneAlgo::setLearningRate(double epsillon)
  186. {
  187. learningRate = epsillon;
  188. }
  189. void tSneAlgo::setPerplexity(double perplexity)
  190. {
  191. this->perplexity = perplexity;
  192. }
  193. // Compute gradient of the t-SNE cost function (using Barnes-Hut algorithm)
  194. static void computeGradient(unsigned int* inp_row_P, unsigned int* inp_col_P, double* inp_val_P, double* Y, int N, int D, double* dC, double theta)
  195. {
  196. // Construct space-partitioning tree on current map
  197. SPTree* tree = new SPTree(D, Y, N);
  198. // Compute all terms required for t-SNE gradient
  199. double sum_Q = .0;
  200. double* pos_f = (double*)calloc(N * D, sizeof(double));
  201. double* neg_f = (double*)calloc(N * D, sizeof(double));
  202. if (pos_f == NULL || neg_f == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  203. tree->computeEdgeForces(inp_row_P, inp_col_P, inp_val_P, N, pos_f);
  204. for (int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, neg_f + n * D, &sum_Q);
  205. // Compute final t-SNE gradient
  206. for (int i = 0; i < N * D; i++) {
  207. dC[i] = pos_f[i] - (neg_f[i] / sum_Q);
  208. }
  209. free(pos_f);
  210. free(neg_f);
  211. delete tree;
  212. }
  213. // Compute gradient of the t-SNE cost function (exact)
  214. static void computeExactGradient(double* P, double* Y, int N, int D, double* dC) {
  215. // Make sure the current gradient contains zeros
  216. for (int i = 0; i < N * D; i++) dC[i] = 0.0;
  217. // Compute the squared Euclidean distance matrix
  218. double* DD = (double*)malloc(N * N * sizeof(double));
  219. if (DD == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  220. computeSquaredEuclideanDistance(Y, N, D, DD);
  221. // Compute Q-matrix and normalization sum
  222. double* Q = (double*)malloc(N * N * sizeof(double));
  223. if (Q == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  224. double sum_Q = .0;
  225. int nN = 0;
  226. for (int n = 0; n < N; n++) {
  227. for (int m = 0; m < N; m++) {
  228. if (n != m) {
  229. Q[nN + m] = 1 / (1 + DD[nN + m]);
  230. sum_Q += Q[nN + m];
  231. }
  232. }
  233. nN += N;
  234. }
  235. // Perform the computation of the gradient
  236. nN = 0;
  237. int nD = 0;
  238. for (int n = 0; n < N; n++) {
  239. int mD = 0;
  240. for (int m = 0; m < N; m++) {
  241. if (n != m) {
  242. double mult = (P[nN + m] - (Q[nN + m] / sum_Q)) * Q[nN + m];
  243. for (int d = 0; d < D; d++) {
  244. dC[nD + d] += (Y[nD + d] - Y[mD + d]) * mult;
  245. }
  246. }
  247. mD += D;
  248. }
  249. nN += N;
  250. nD += D;
  251. }
  252. // Free memory
  253. free(DD); DD = NULL;
  254. free(Q); Q = NULL;
  255. }
  256. // Evaluate t-SNE cost function (exactly)
  257. static double evaluateError(double* P, double* Y, int N, int D) {
  258. // Compute the squared Euclidean distance matrix
  259. double* DD = (double*)malloc(N * N * sizeof(double));
  260. double* Q = (double*)malloc(N * N * sizeof(double));
  261. if (DD == NULL || Q == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  262. computeSquaredEuclideanDistance(Y, N, D, DD);
  263. // Compute Q-matrix and normalization sum
  264. int nN = 0;
  265. double sum_Q = DBL_MIN;
  266. for (int n = 0; n < N; n++) {
  267. for (int m = 0; m < N; m++) {
  268. if (n != m) {
  269. Q[nN + m] = 1 / (1 + DD[nN + m]);
  270. sum_Q += Q[nN + m];
  271. }
  272. else Q[nN + m] = DBL_MIN;
  273. }
  274. nN += N;
  275. }
  276. for (int i = 0; i < N * N; i++) Q[i] /= sum_Q;
  277. // Sum t-SNE error
  278. double C = .0;
  279. for (int n = 0; n < N * N; n++) {
  280. C += P[n] * log((P[n] + FLT_MIN) / (Q[n] + FLT_MIN));
  281. }
  282. // Clean up memory
  283. free(DD);
  284. free(Q);
  285. return C;
  286. }
  287. // Evaluate t-SNE cost function (approximately)
  288. static double evaluateError(unsigned int* row_P, unsigned int* col_P, double* val_P, double* Y, int N, int D, double theta)
  289. {
  290. // Get estimate of normalization term
  291. SPTree* tree = new SPTree(D, Y, N);
  292. double* buff = (double*)calloc(D, sizeof(double));
  293. double sum_Q = .0;
  294. for (int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, buff, &sum_Q);
  295. // Loop over all edges to compute t-SNE error
  296. int ind1, ind2;
  297. double C = .0, Q;
  298. for (int n = 0; n < N; n++) {
  299. ind1 = n * D;
  300. for (int i = row_P[n]; i < row_P[n + 1]; i++) {
  301. Q = .0;
  302. ind2 = col_P[i] * D;
  303. for (int d = 0; d < D; d++) buff[d] = Y[ind1 + d];
  304. for (int d = 0; d < D; d++) buff[d] -= Y[ind2 + d];
  305. for (int d = 0; d < D; d++) Q += buff[d] * buff[d];
  306. Q = (1.0 / (1.0 + Q)) / sum_Q;
  307. C += val_P[i] * log((val_P[i] + FLT_MIN) / (Q + FLT_MIN));
  308. }
  309. }
  310. // Clean up memory
  311. free(buff);
  312. delete tree;
  313. return C;
  314. }
  315. // Compute input similarities with a fixed perplexity
  316. static void computeGaussianPerplexity(double* inputArrayX, int N, int D, double* P, double perplexity) {
  317. // Compute the squared Euclidean distance matrix
  318. double* DD = (double*)malloc(N * N * sizeof(double));
  319. if (DD == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  320. computeSquaredEuclideanDistance(inputArrayX, N, D, DD);
  321. // Compute the Gaussian kernel row by row
  322. int nN = 0;
  323. for (int n = 0; n < N; n++) {
  324. // Initialize some variables
  325. bool found = false;
  326. double beta = 1.0;
  327. double min_beta = -DBL_MAX;
  328. double max_beta = DBL_MAX;
  329. double tol = 1e-5;
  330. double sum_P;
  331. // Iterate until we found a good perplexity
  332. int iter = 0;
  333. while (!found && iter < 200) {
  334. // Compute Gaussian kernel row
  335. for (int m = 0; m < N; m++) P[nN + m] = exp(-beta * DD[nN + m]);
  336. P[nN + n] = DBL_MIN;
  337. // Compute entropy of current row
  338. sum_P = DBL_MIN;
  339. for (int m = 0; m < N; m++) sum_P += P[nN + m];
  340. double H = 0.0;
  341. for (int m = 0; m < N; m++) H += beta * (DD[nN + m] * P[nN + m]);
  342. H = (H / sum_P) + log(sum_P);
  343. // Evaluate whether the entropy is within the tolerance level
  344. double Hdiff = H - log(perplexity);
  345. if (Hdiff < tol && -Hdiff < tol) {
  346. found = true;
  347. }
  348. else {
  349. if (Hdiff > 0) {
  350. min_beta = beta;
  351. if (max_beta == DBL_MAX || max_beta == -DBL_MAX)
  352. beta *= 2.0;
  353. else
  354. beta = (beta + max_beta) / 2.0;
  355. }
  356. else {
  357. max_beta = beta;
  358. if (min_beta == -DBL_MAX || min_beta == DBL_MAX)
  359. beta /= 2.0;
  360. else
  361. beta = (beta + min_beta) / 2.0;
  362. }
  363. }
  364. // Update iteration counter
  365. iter++;
  366. }
  367. // Row normalize P
  368. for (int m = 0; m < N; m++) P[nN + m] /= sum_P;
  369. nN += N;
  370. }
  371. // Clean up memory
  372. free(DD); DD = NULL;
  373. }
  374. // Compute input similarities with a fixed perplexity using ball trees (this function allocates memory another function should free)
  375. static void computeGaussianPerplexity(double* inputArrayX, int N, int D, unsigned int** _row_P, unsigned int** _col_P, double** _val_P, double perplexity, int K) {
  376. if (perplexity > K) printf("Perplexity should be lower than K!\n");
  377. // Allocate the memory we need
  378. *_row_P = (unsigned int*)malloc((N + 1) * sizeof(unsigned int));
  379. *_col_P = (unsigned int*)calloc(N * K, sizeof(unsigned int));
  380. *_val_P = (double*)calloc(N * K, sizeof(double));
  381. if (*_row_P == NULL || *_col_P == NULL || *_val_P == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  382. unsigned int* row_P = *_row_P;
  383. unsigned int* col_P = *_col_P;
  384. double* val_P = *_val_P;
  385. double* cur_P = (double*)malloc((N - 1) * sizeof(double));
  386. if (cur_P == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  387. row_P[0] = 0;
  388. for (int n = 0; n < N; n++) row_P[n + 1] = row_P[n] + (unsigned int)K;
  389. // Build ball tree on data set
  390. VpTree<DataPoint, euclidean_distance>* tree = new VpTree<DataPoint, euclidean_distance>();
  391. vector<DataPoint> obj_X(N, DataPoint(D, -1, inputArrayX));
  392. for (int n = 0; n < N; n++) obj_X[n] = DataPoint(D, n, inputArrayX + n * D);
  393. tree->create(obj_X);
  394. // Loop over all points to find nearest neighbors
  395. printf("Building tree...\n");
  396. vector<DataPoint> indices;
  397. vector<double> distances;
  398. for (int n = 0; n < N; n++) {
  399. if (n % 10000 == 0) printf(" - point %d of %d\n", n, N);
  400. // Find nearest neighbors
  401. indices.clear();
  402. distances.clear();
  403. tree->search(obj_X[n], K + 1, &indices, &distances);
  404. // Initialize some variables for binary search
  405. bool found = false;
  406. double beta = 1.0;
  407. double min_beta = -DBL_MAX;
  408. double max_beta = DBL_MAX;
  409. double tol = 1e-5;
  410. // Iterate until we found a good perplexity
  411. int iter = 0; double sum_P;
  412. while (!found && iter < 200) {
  413. // Compute Gaussian kernel row
  414. for (int m = 0; m < K; m++) cur_P[m] = exp(-beta * distances[m + 1] * distances[m + 1]);
  415. // Compute entropy of current row
  416. sum_P = DBL_MIN;
  417. for (int m = 0; m < K; m++) sum_P += cur_P[m];
  418. double H = .0;
  419. for (int m = 0; m < K; m++) H += beta * (distances[m + 1] * distances[m + 1] * cur_P[m]);
  420. H = (H / sum_P) + log(sum_P);
  421. // Evaluate whether the entropy is within the tolerance level
  422. double Hdiff = H - log(perplexity);
  423. if (Hdiff < tol && -Hdiff < tol) {
  424. found = true;
  425. }
  426. else {
  427. if (Hdiff > 0) {
  428. min_beta = beta;
  429. if (max_beta == DBL_MAX || max_beta == -DBL_MAX)
  430. beta *= 2.0;
  431. else
  432. beta = (beta + max_beta) / 2.0;
  433. }
  434. else {
  435. max_beta = beta;
  436. if (min_beta == -DBL_MAX || min_beta == DBL_MAX)
  437. beta /= 2.0;
  438. else
  439. beta = (beta + min_beta) / 2.0;
  440. }
  441. }
  442. // Update iteration counter
  443. iter++;
  444. }
  445. // Row-normalize current row of P and store in matrix
  446. for (unsigned int m = 0; m < K; m++) cur_P[m] /= sum_P;
  447. for (unsigned int m = 0; m < K; m++) {
  448. col_P[row_P[n] + m] = (unsigned int)indices[m + 1].index();
  449. val_P[row_P[n] + m] = cur_P[m];
  450. }
  451. }
  452. // Clean up memory
  453. obj_X.clear();
  454. free(cur_P);
  455. delete tree;
  456. }
  457. // Symmetrizes a sparse matrix
  458. static void symmetrizeMatrix(unsigned int** _row_P, unsigned int** _col_P, double** _val_P, int N) {
  459. // Get sparse matrix
  460. unsigned int* row_P = *_row_P;
  461. unsigned int* col_P = *_col_P;
  462. double* val_P = *_val_P;
  463. // Count number of elements and row counts of symmetric matrix
  464. int* row_counts = (int*)calloc(N, sizeof(int));
  465. if (row_counts == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  466. for (int n = 0; n < N; n++) {
  467. for (int i = row_P[n]; i < row_P[n + 1]; i++) {
  468. // Check whether element (col_P[i], n) is present
  469. bool present = false;
  470. for (int m = row_P[col_P[i]]; m < row_P[col_P[i] + 1]; m++) {
  471. if (col_P[m] == n) present = true;
  472. }
  473. if (present) row_counts[n]++;
  474. else {
  475. row_counts[n]++;
  476. row_counts[col_P[i]]++;
  477. }
  478. }
  479. }
  480. int no_elem = 0;
  481. for (int n = 0; n < N; n++) no_elem += row_counts[n];
  482. // Allocate memory for symmetrized matrix
  483. unsigned int* sym_row_P = (unsigned int*)malloc((N + 1) * sizeof(unsigned int));
  484. unsigned int* sym_col_P = (unsigned int*)malloc(no_elem * sizeof(unsigned int));
  485. double* sym_val_P = (double*)malloc(no_elem * sizeof(double));
  486. if (sym_row_P == NULL || sym_col_P == NULL || sym_val_P == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  487. // Construct new row indices for symmetric matrix
  488. sym_row_P[0] = 0;
  489. for (int n = 0; n < N; n++) sym_row_P[n + 1] = sym_row_P[n] + (unsigned int)row_counts[n];
  490. // Fill the result matrix
  491. int* offset = (int*)calloc(N, sizeof(int));
  492. if (offset == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  493. for (int n = 0; n < N; n++) {
  494. for (unsigned int i = row_P[n]; i < row_P[n + 1]; i++) { // considering element(n, col_P[i])
  495. // Check whether element (col_P[i], n) is present
  496. bool present = false;
  497. for (unsigned int m = row_P[col_P[i]]; m < row_P[col_P[i] + 1]; m++) {
  498. if (col_P[m] == n) {
  499. present = true;
  500. if (n <= col_P[i]) { // make sure we do not add elements twice
  501. sym_col_P[sym_row_P[n] + offset[n]] = col_P[i];
  502. sym_col_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = n;
  503. sym_val_P[sym_row_P[n] + offset[n]] = val_P[i] + val_P[m];
  504. sym_val_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = val_P[i] + val_P[m];
  505. }
  506. }
  507. }
  508. // If (col_P[i], n) is not present, there is no addition involved
  509. if (!present) {
  510. sym_col_P[sym_row_P[n] + offset[n]] = col_P[i];
  511. sym_col_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = n;
  512. sym_val_P[sym_row_P[n] + offset[n]] = val_P[i];
  513. sym_val_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = val_P[i];
  514. }
  515. // Update offsets
  516. if (!present || (present && n <= col_P[i])) {
  517. offset[n]++;
  518. if (col_P[i] != n) offset[col_P[i]]++;
  519. }
  520. }
  521. }
  522. // Divide the result by two
  523. for (int i = 0; i < no_elem; i++) sym_val_P[i] /= 2.0;
  524. // Return symmetrized matrices
  525. free(*_row_P); *_row_P = sym_row_P;
  526. free(*_col_P); *_col_P = sym_col_P;
  527. free(*_val_P); *_val_P = sym_val_P;
  528. // Free up some memery
  529. free(offset); offset = NULL;
  530. free(row_counts); row_counts = NULL;
  531. }
  532. // Compute squared Euclidean distance matrix
  533. static void computeSquaredEuclideanDistance(double* inputArrayX, int N, int D, double* DD) {
  534. const double* XnD = inputArrayX;
  535. for (int n = 0; n < N; ++n, XnD += D) {
  536. const double* XmD = XnD + D;
  537. double* curr_elem = &DD[n * N + n];
  538. *curr_elem = 0.0;
  539. double* curr_elem_sym = curr_elem + N;
  540. for (int m = n + 1; m < N; ++m, XmD += D, curr_elem_sym += N) {
  541. *(++curr_elem) = 0.0;
  542. for (int d = 0; d < D; ++d) {
  543. *curr_elem += (XnD[d] - XmD[d]) * (XnD[d] - XmD[d]);
  544. }
  545. *curr_elem_sym = *curr_elem;
  546. }
  547. }
  548. }
  549. // Makes data zero-mean
  550. static void zeroMean(double* inputArrayX, int N, int D) {
  551. // Compute data mean
  552. double* mean = (double*)calloc(D, sizeof(double));
  553. if (mean == NULL) { printf("Memory allocation failed!\n"); exit(1); }
  554. int nD = 0;
  555. for (int n = 0; n < N; n++) {
  556. for (int d = 0; d < D; d++) {
  557. mean[d] += inputArrayX[nD + d];
  558. }
  559. nD += D;
  560. }
  561. for (int d = 0; d < D; d++) {
  562. mean[d] /= (double)N;
  563. }
  564. // Subtract data mean
  565. nD = 0;
  566. for (int n = 0; n < N; n++) {
  567. for (int d = 0; d < D; d++) {
  568. inputArrayX[nD + d] -= mean[d];
  569. }
  570. nD += D;
  571. }
  572. free(mean); mean = NULL;
  573. }
  574. // Generates a Gaussian random number
  575. static double randn() {
  576. double inputArrayX, y, radius;
  577. do {
  578. inputArrayX = 2 * (rand() / ((double)RAND_MAX + 1)) - 1;
  579. y = 2 * (rand() / ((double)RAND_MAX + 1)) - 1;
  580. radius = (inputArrayX * inputArrayX) + (y * y);
  581. } while ((radius >= 1.0) || (radius == 0.0));
  582. radius = sqrt(-2 * log(radius) / radius);
  583. inputArrayX *= radius;
  584. y *= radius;
  585. return inputArrayX;
  586. }