Skip to content

Commit 86ba173

Browse files
author
Lance Hepler
committed
fix reviewer comments
this stuffs most pointer badness into extern "C" blocks
1 parent 00bc9fc commit 86ba173

2 files changed

Lines changed: 92 additions & 98 deletions

File tree

tsne/bh_sne_src/tsne.cpp

Lines changed: 90 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <cstdio>
4141
#include <cstdlib>
4242
#include <ctime>
43+
#include <memory>
4344
#include <utility>
4445
#include <vector>
4546

@@ -55,9 +56,30 @@ using std::fprintf;
5556
using std::log;
5657
using std::move;
5758
using std::sqrt;
59+
using std::unique_ptr;
5860
using std::vector;
5961

62+
namespace {
63+
6064
struct TSNEState {
65+
public:
66+
bool step_by(int n);
67+
68+
private:
69+
template <int NDIMS>
70+
bool step_by_impl(int n);
71+
72+
public:
73+
int N;
74+
double* Y;
75+
int no_dims;
76+
double theta;
77+
int max_iter;
78+
int stop_lying_iter;
79+
int mom_switch_iter;
80+
int iter;
81+
float total_time;
82+
float residual_time;
6183
std::vector<double> P;
6284
std::vector<unsigned int> row_P;
6385
std::vector<unsigned int> col_P;
@@ -67,8 +89,6 @@ struct TSNEState {
6789
std::vector<double> gains;
6890
};
6991

70-
namespace {
71-
7292
template <int NDIMS>
7393
void run(double* X, int N, int D, double* Y, double perplexity, double theta,
7494
int rand_seed, bool skip_random_init, double* init, bool use_init,
@@ -589,10 +609,11 @@ double randn() {
589609

590610
// Initialize t-SNE
591611
template <int NDIMS>
592-
struct TSNE* init_tsne(double* X, int N, int D, double* Y, double perplexity,
593-
double theta, int rand_seed, bool skip_random_init,
594-
double* init, bool use_init, int max_iter,
595-
int stop_lying_iter, int mom_switch_iter) {
612+
unique_ptr<TSNEState> make_tsne_impl(double* X, int N, int D, double* Y,
613+
double perplexity, double theta,
614+
int rand_seed, bool skip_random_init,
615+
double* init, bool use_init, int max_iter,
616+
int stop_lying_iter, int mom_switch_iter) {
596617
// Set random seed
597618
if (skip_random_init != true) {
598619
if (rand_seed >= 0) {
@@ -715,59 +736,34 @@ struct TSNE* init_tsne(double* X, int N, int D, double* Y, double perplexity,
715736
(float)(end - start) / CLOCKS_PER_SEC,
716737
(double)row_P[N] / ((double)N * (double)N));
717738

718-
TSNEState* state = new TSNEState;
719-
state->P = P;
720-
state->row_P = row_P;
721-
state->col_P = col_P;
722-
state->val_P = val_P;
723-
state->dY = dY;
724-
state->uY = uY;
725-
state->gains = gains;
726-
TSNE* tsne = new TSNE;
727-
tsne->N = N;
728-
tsne->Y = Y;
729-
tsne->no_dims = NDIMS;
730-
tsne->theta = theta;
731-
tsne->max_iter = max_iter;
732-
tsne->stop_lying_iter = stop_lying_iter;
733-
tsne->mom_switch_iter = mom_switch_iter;
734-
tsne->state = state;
735-
tsne->iter = 0;
736-
tsne->total_time = 0;
737-
tsne->residual_time = 0;
739+
auto tsne = std::unique_ptr<TSNEState>(new TSNEState{
740+
N, Y, NDIMS, theta, max_iter, stop_lying_iter, mom_switch_iter, 0, .0, .0,
741+
P, row_P, col_P, val_P, dY, uY, gains});
738742

739743
return tsne;
740744
}
741745

742746
// Optimize t-SNE
743747
template <int NDIMS>
744-
bool run_n(int n, struct TSNE* tsne) {
748+
bool TSNEState::step_by_impl(int step) {
745749
// Set learning parameters
746750
double momentum = .5, final_momentum = .8;
747751
double eta = 200.0;
748752

749753
// Extract state
750-
int N = tsne->N;
751-
double* Y = tsne->Y;
752-
double theta = tsne->theta;
753754
bool exact = (theta == .0) ? true : false;
754-
TSNEState* state = tsne->state;
755-
vector<double>& dY = state->dY;
756-
vector<double>& uY = state->uY;
757755
clock_t start = clock(), end;
758756
float elapsed;
759-
int max_iter = std::min(tsne->iter + n, tsne->max_iter);
757+
int iter_until = std::min(iter + step, max_iter);
760758

761-
for (int& iter = tsne->iter; iter < max_iter; iter++) {
759+
for (; iter < iter_until; iter++) {
762760
// Compute (approximate) gradient
763761
if (exact)
764-
computeExactGradient<NDIMS>(state->P, Y, N, dY);
762+
computeExactGradient<NDIMS>(P, Y, N, dY);
765763
else
766-
computeGradient<NDIMS>(state->row_P, state->col_P, state->val_P, Y, N, dY,
767-
theta);
764+
computeGradient<NDIMS>(row_P, col_P, val_P, Y, N, dY, theta);
768765

769766
// Update gains
770-
vector<double>& gains = state->gains;
771767
for (int i = 0; i < N * NDIMS; i++)
772768
gains[i] =
773769
(sign(dY[i]) != sign(uY[i])) ? (gains[i] + .2) : (gains[i] * .8);
@@ -785,105 +781,117 @@ bool run_n(int n, struct TSNE* tsne) {
785781
zeroMean<NDIMS>(Y, N);
786782

787783
// Stop lying about the P-values after a while, and switch momentum
788-
if (iter == tsne->stop_lying_iter) {
784+
if (iter == stop_lying_iter) {
789785
if (exact) {
790786
for (int i = 0; i < N * N; i++)
791-
state->P[i] /= 12.0;
787+
P[i] /= 12.0;
792788
} else {
793-
for (int i = 0; i < state->row_P[N]; i++)
794-
state->val_P[i] /= 12.0;
789+
for (int i = 0; i < row_P[N]; i++)
790+
val_P[i] /= 12.0;
795791
}
796792
}
797-
if (iter == tsne->mom_switch_iter)
793+
if (iter == mom_switch_iter)
798794
momentum = final_momentum;
799795

800796
// Print out progress
801-
if (iter > 0 && (iter % 50 == 0 || iter == tsne->max_iter - 1)) {
797+
if (iter > 0 && (iter % 50 == 0 || iter == max_iter - 1)) {
802798
end = clock();
803799
double C = .0;
804800
if (exact) {
805-
C = evaluateError<NDIMS>(state->P, Y, N);
801+
C = evaluateError<NDIMS>(P, Y, N);
806802
} else {
807803
// doing approximate computation here!
808-
C = evaluateError<NDIMS>(state->row_P, state->col_P, state->val_P, Y, N,
809-
theta);
804+
C = evaluateError<NDIMS>(row_P, col_P, val_P, Y, N, theta);
810805
}
811806
if (iter == 0)
812807
fprintf(stderr, "Iteration %d: error is %f\n", iter + 1, C);
813808
else {
814809
elapsed = (float)(end - start) / CLOCKS_PER_SEC;
815-
tsne->total_time += elapsed;
816-
tsne->residual_time += elapsed;
810+
total_time += elapsed;
811+
residual_time += elapsed;
817812
fprintf(stderr,
818813
"Iteration %d: error is %f (50 iterations in %4.2f seconds)\n",
819-
iter, C, tsne->residual_time);
820-
tsne->residual_time = 0;
814+
iter, C, residual_time);
815+
residual_time = 0;
821816
}
822817
start = clock();
823818
}
824819
}
825820
end = clock();
826821
elapsed = (float)(end - start) / CLOCKS_PER_SEC;
827-
tsne->total_time += elapsed;
828-
tsne->residual_time += elapsed;
822+
total_time += elapsed;
823+
residual_time += elapsed;
829824

830-
if (tsne->iter >= tsne->max_iter) {
831-
fprintf(stderr, "Fitting performed in %4.2f seconds.\n", tsne->total_time);
825+
if (iter >= max_iter) {
826+
fprintf(stderr, "Fitting performed in %4.2f seconds.\n", total_time);
832827
return true;
833828
}
834829
return false;
835830
}
836831

837-
} // namespace
838-
839-
extern "C" {
840-
struct TSNE* DLL_PUBLIC init_tsne(double* X, int N, int D, double* Y,
841-
int no_dims, double perplexity, double theta,
842-
int rand_seed, bool skip_random_init,
843-
double* init, bool use_init, int max_iter,
844-
int stop_lying_iter, int mom_switch_iter) {
845-
assert(no_dims == 2 || no_dims == 3);
832+
unique_ptr<TSNEState> make_tsne(double* X, int N, int D, double* Y, int no_dims,
833+
double perplexity, double theta, int rand_seed,
834+
bool skip_random_init, double* init,
835+
bool use_init, int max_iter,
836+
int stop_lying_iter, int mom_switch_iter) {
846837
switch (no_dims) {
847838
case 2:
848-
return init_tsne<2>(X, N, D, Y, perplexity, theta, rand_seed,
849-
skip_random_init, init, use_init, max_iter,
850-
stop_lying_iter, mom_switch_iter);
839+
return make_tsne_impl<2>(X, N, D, Y, perplexity, theta, rand_seed,
840+
skip_random_init, init, use_init, max_iter,
841+
stop_lying_iter, mom_switch_iter);
851842
case 3:
852-
return init_tsne<3>(X, N, D, Y, perplexity, theta, rand_seed,
853-
skip_random_init, init, use_init, max_iter,
854-
stop_lying_iter, mom_switch_iter);
843+
return make_tsne_impl<3>(X, N, D, Y, perplexity, theta, rand_seed,
844+
skip_random_init, init, use_init, max_iter,
845+
stop_lying_iter, mom_switch_iter);
855846
default:
856847
throw "invalid dimension";
857848
}
858849
}
859850

860-
bool DLL_PUBLIC run_n(int n, struct TSNE* tsne) {
861-
switch (tsne->no_dims) {
851+
bool TSNEState::step_by(int step) {
852+
switch (no_dims) {
862853
case 2:
863-
return run_n<2>(n, tsne);
854+
return step_by_impl<2>(step);
864855
case 3:
865-
return run_n<3>(n, tsne);
856+
return step_by_impl<3>(step);
866857
default:
867858
throw "invalid dimension";
868859
}
869860
}
870861

862+
} // namespace
863+
864+
extern "C" {
865+
struct TSNE* DLL_PUBLIC init_tsne(double* X, int N, int D, double* Y,
866+
int no_dims, double perplexity, double theta,
867+
int rand_seed, bool skip_random_init,
868+
double* init, bool use_init, int max_iter,
869+
int stop_lying_iter, int mom_switch_iter) {
870+
assert(no_dims == 2 || no_dims == 3);
871+
auto tsne = make_tsne(X, N, D, Y, no_dims, perplexity, theta, rand_seed,
872+
skip_random_init, init, use_init, max_iter,
873+
stop_lying_iter, mom_switch_iter);
874+
return (TSNE*)tsne.release();
875+
}
876+
877+
bool DLL_PUBLIC step_tsne_by(struct TSNE* tsne, int step) {
878+
return ((TSNEState*)tsne)->step_by(step);
879+
}
880+
871881
void DLL_PUBLIC free_tsne(struct TSNE* tsne) {
872882
if (tsne == nullptr)
873883
return;
874-
delete tsne->state;
875-
delete tsne;
884+
delete (TSNEState*)tsne;
876885
}
877886

878887
void DLL_PUBLIC run(double* X, int N, int D, double* Y, int no_dims,
879888
double perplexity, double theta, int rand_seed,
880889
bool skip_random_init, double* init, bool use_init,
881890
int max_iter, int stop_lying_iter, int mom_switch_iter) {
882-
struct TSNE* tsne = init_tsne(X, N, D, Y, no_dims, perplexity, theta,
883-
rand_seed, skip_random_init, init, use_init,
884-
max_iter, stop_lying_iter, mom_switch_iter);
885-
run_n(max_iter, tsne);
886-
free_tsne(tsne);
891+
auto tsne = make_tsne(X, N, D, Y, no_dims, perplexity, theta, rand_seed,
892+
skip_random_init, init, use_init, max_iter,
893+
stop_lying_iter, mom_switch_iter);
894+
tsne->step_by(max_iter);
887895
}
888896

889897
} // extern "C"

tsne/bh_sne_src/tsne.h

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,15 @@ void DLL_PUBLIC run(double* X, int N, int D, double* Y, int no_dims,
5656
int max_iter = 1000, int stop_lying_iter = 250,
5757
int mom_switch_iter = 250);
5858
// stateful t-SNE
59-
struct TSNEState;
60-
struct TSNE {
61-
// parameters
62-
int N;
63-
double* Y;
64-
int no_dims;
65-
double theta;
66-
int max_iter;
67-
int stop_lying_iter;
68-
int mom_switch_iter;
69-
struct TSNEState* state;
70-
int iter;
71-
float total_time;
72-
float residual_time;
73-
};
59+
struct TSNE;
7460
struct TSNE* DLL_PUBLIC init_tsne(double* X, int N, int D, double* Y,
7561
int no_dims, double perplexity, double theta,
7662
int rand_seed, bool skip_random_init,
7763
double* init, bool use_init,
7864
int max_iter = 1000,
7965
int stop_lying_iter = 250,
8066
int mom_switch_iter = 250);
81-
bool DLL_PUBLIC run_n(int n, struct TSNE* tsne);
67+
bool DLL_PUBLIC step_tsne_by(struct TSNE* tsne, int step);
8268
void DLL_PUBLIC free_tsne(struct TSNE* tsne);
8369
}
8470

0 commit comments

Comments
 (0)