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;
5556using std::log;
5657using std::move;
5758using std::sqrt;
59+ using std::unique_ptr;
5860using std::vector;
5961
62+ namespace {
63+
6064struct 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-
7292template <int NDIMS >
7393void 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
591611template <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
743747template <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+
871881void 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
878887void 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"
0 commit comments