// Evaluate t-SNE cost function (approximately) double TSNE::evaluateError(int* row_P, int* col_P, double* val_P, double* Y, int N, int D, double theta) { // Get estimate of normalization term SPTree* tree = new SPTree(D, Y, N); double* buff = (double*) calloc(D, sizeof(double)); double sum_Q = .0; for(int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, buff, &sum_Q); // Loop over all edges to compute t-SNE error int ind1, ind2; double C = .0, Q; for(int n = 0; n < N; n++) { ind1 = n * D; for(int i = row_P[n]; i < row_P[n + 1]; i++) { Q = .0; ind2 = col_P[i] * D; for(int d = 0; d < D; d++) buff[d] = Y[ind1 + d]; for(int d = 0; d < D; d++) buff[d] -= Y[ind2 + d]; for(int d = 0; d < D; d++) Q += buff[d] * buff[d]; Q = (1.0 / (1.0 + Q)) / sum_Q; C += val_P[i] * log((val_P[i] + FLT_MIN) / (Q + FLT_MIN)); } } // Clean up memory free(buff); delete tree; return C; }
void TSNE<NDims>::getCost(unsigned int* row_P, unsigned int* col_P, double* val_P, double* Y, unsigned int N, int D, double theta, double* costs) { // Get estimate of normalization term SPTree<NDims>* tree = new SPTree<NDims>(Y, N); double* buff = (double*) calloc(D, sizeof(double)); double sum_Q = .0; for(unsigned int n = 0; n < N; n++) sum_Q += tree->computeNonEdgeForces(n, theta, buff); // Loop over all edges to compute t-SNE error int ind1, ind2; double Q; for(unsigned int n = 0; n < N; n++) { ind1 = n * D; costs[n] = 0.0; for(unsigned int i = row_P[n]; i < row_P[n + 1]; i++) { Q = .0; ind2 = col_P[i] * D; for(int d = 0; d < D; d++) buff[d] = Y[ind1 + d]; for(int d = 0; d < D; d++) buff[d] -= Y[ind2 + d]; for(int d = 0; d < D; d++) Q += buff[d] * buff[d]; Q = (1.0 / (1.0 + Q)) / sum_Q; costs[n] += val_P[i] * log((val_P[i] + FLT_MIN) / (Q + FLT_MIN)); } } // Clean up memory free(buff); delete tree; }
void TSNE<NDims>::computeGradient(double* P, unsigned int* inp_row_P, unsigned int* inp_col_P, double* inp_val_P, double* Y, unsigned int N, int D, double* dC, double theta) { // Construct space-partitioning tree on current map SPTree<NDims>* tree = new SPTree<NDims>(Y, N); // Compute all terms required for t-SNE gradient double* pos_f = (double*) calloc(N * D, sizeof(double)); double* neg_f = (double*) calloc(N * D, sizeof(double)); if(pos_f == NULL || neg_f == NULL) { Rcpp::stop("Memory allocation failed!\n"); } tree->computeEdgeForces(inp_row_P, inp_col_P, inp_val_P, N, pos_f, num_threads); // Storing the output to sum in single-threaded mode; avoid randomness in rounding errors. std::vector<double> output(N); #pragma omp parallel for schedule(guided) num_threads(num_threads) for (unsigned int n = 0; n < N; n++) { output[n]=tree->computeNonEdgeForces(n, theta, neg_f + n * D); } double sum_Q = .0; for (unsigned int n=0; n<N; ++n) { sum_Q += output[n]; } // Compute final t-SNE gradient for(unsigned int i = 0; i < N * D; i++) { dC[i] = pos_f[i] - (neg_f[i] / sum_Q); } free(pos_f); free(neg_f); delete tree; }
// Compute gradient of the t-SNE cost function (using Barnes-Hut algorithm) void TSNE::computeGradient(double* P, int* inp_row_P, int* inp_col_P, double* inp_val_P, double* Y, int N, int D, double* dC, double theta) { // Construct space-partitioning tree on current map SPTree* tree = new SPTree(D, Y, N); // Compute all terms required for t-SNE gradient double sum_Q = .0; double* pos_f = (double*) calloc(N * D, sizeof(double)); double* neg_f = (double*) calloc(N * D, sizeof(double)); if(pos_f == NULL || neg_f == NULL) { cout<<"Memory allocation failed!\n"; } tree->computeEdgeForces(inp_row_P, inp_col_P, inp_val_P, N, pos_f); for(int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, neg_f + n * D, &sum_Q); // Compute final t-SNE gradient for(int i = 0; i < N * D; i++) { dC[i] = pos_f[i] - (neg_f[i] / sum_Q); } free(pos_f); free(neg_f); delete tree; }