From 33a995e116b76dfd8bca88210edf1b595bb285be Mon Sep 17 00:00:00 2001 From: vasiloglou Date: Thu, 17 Jan 2008 05:06:56 +0000 Subject: [PATCH] it is running --- fastlib/u/nvasil/kernel_pca/allknn.h | 31 ++++--- fastlib/u/nvasil/kernel_pca/build.py | 2 +- fastlib/u/nvasil/kernel_pca/kernel_pca.h | 19 ++-- fastlib/u/nvasil/kernel_pca/kernel_pca_impl.h | 83 ++++++++---------- .../u/nvasil/kernel_pca/kernel_pca_test.cc | 4 +- fastlib/u/nvasil/kernel_pca/test_data_3_1000 | Bin 12004 -> 0 bytes .../u/nvasil/kernel_pca/test_data_3_1000.ind | Bin 8000 -> 0 bytes 7 files changed, 64 insertions(+), 75 deletions(-) delete mode 100755 fastlib/u/nvasil/kernel_pca/test_data_3_1000 delete mode 100755 fastlib/u/nvasil/kernel_pca/test_data_3_1000.ind diff --git a/fastlib/u/nvasil/kernel_pca/allknn.h b/fastlib/u/nvasil/kernel_pca/allknn.h index d28a0fc2f0..808e93ad7f 100644 --- a/fastlib/u/nvasil/kernel_pca/allknn.h +++ b/fastlib/u/nvasil/kernel_pca/allknn.h @@ -115,7 +115,7 @@ class AllkNN { /////////////////////////////// Constructors ///////////////////////////////////////////// // Add this at the beginning of a class to prevent accidentally calling the copy constructor - FORBID_ACCIDENTAL_COPIES(AllNN); + FORBID_ACCIDENTAL_COPIES(AllkNN); public: @@ -171,7 +171,7 @@ class AllkNN { // Used to find the query node's new upper bound double query_max_neighbor_distance = -1.0; - ArrayList > neighbors; + ArrayList > neighbors; neighbors.Init(knns_, knns_+leaf_size_); // node->begin() is the index of the first point in the node, // node->end is one past the last index @@ -184,8 +184,8 @@ class AllkNN { index_t ind = query_index*knns_; for(index_t i=0; ibegin(); @@ -206,13 +206,13 @@ class AllkNN { if (neighbors.size()>knns_) { std::sort(neighbors.begin(), neighbors.end()); for(index_t i=0; i query_max_neighbor_distance) { - query_max_neighbor_distance = neighbor_distances_[nd+knns_-1]; + if (neighbor_distances_[ind+knns_-1] > query_max_neighbor_distance) { + query_max_neighbor_distance = neighbor_distances_[ind+knns_-1]; } } @@ -344,7 +344,7 @@ class AllkNN { * local copies of the data. */ void Init(const Matrix& queries_in, const Matrix& references_in, - index_t leaf_size_, ndex_t knns) { + index_t leaf_size, index_t knns) { // track the number of prunes @@ -422,14 +422,16 @@ class AllkNN { MinNodeDistSq_(query_tree_, reference_tree_)); // We need to initialize the results list before filling it - results->Init(neighbor_indices_.size()); + resulting_neighbors->Init(neighbor_indices_.size()); // We need to map the indices back from how they have // been permuted for (index_t i = 0; i < neighbor_indices_.size(); i++) { - (*results)[old_from_new_queries_[i]] = + (*resulting_neighbors)[ + old_from_new_queries_[i/knns_]*knns_+ i%knns_] = old_from_new_references_[neighbor_indices_[i]]; } - distances.Copy(neighbor_distances_.ptr(), neighbor_distances.len()); + distances->Copy(neighbor_distances_.ptr(), + neighbor_distances_.length()); } // ComputeNeighbors @@ -444,10 +446,11 @@ class AllkNN { // The same code as above results->Init(neighbor_indices_.size()); for (index_t i = 0; i < neighbor_indices_.size(); i++) { - (*results)[old_from_new_queries_[i]] = + (*results)[old_from_new_queries_[i/knns_]] = old_from_new_references_[neighbor_indices_[i]]; } - distances.Copy(neighbor_distances_.ptr(), neighbor_distances.len()); + distances->Copy(neighbor_distances_.ptr(), + neighbor_distances_.length()); } // ComputeNaive }; //class AllNN diff --git a/fastlib/u/nvasil/kernel_pca/build.py b/fastlib/u/nvasil/kernel_pca/build.py index 4b2eb4365c..2807f62e2f 100644 --- a/fastlib/u/nvasil/kernel_pca/build.py +++ b/fastlib/u/nvasil/kernel_pca/build.py @@ -1,5 +1,5 @@ binrule(name="kptest", - headers=["kernel_pca.h", "kernel_pca_impl.h"], + headers=["kernel_pca.h", "kernel_pca_impl.h", "allknn.h"], sources=["kernel_pca_test.cc"], cflags=" -fexceptions", deplibs=["sparse:sparse", "fastlib:fastlib", "la:la"] diff --git a/fastlib/u/nvasil/kernel_pca/kernel_pca.h b/fastlib/u/nvasil/kernel_pca/kernel_pca.h index 9f923dafca..c17a875576 100644 --- a/fastlib/u/nvasil/kernel_pca/kernel_pca.h +++ b/fastlib/u/nvasil/kernel_pca/kernel_pca.h @@ -25,17 +25,14 @@ #include #include "fastlib/fastlib.h" #include "la/matrix.h" -#include "u/nvasil/tree/binary_kd_tree_mmapmm.h" -#include "u/nvasil/dataset/binary_dataset.h" #include "sparse/sparse_matrix.h" +#include "allknn.h" class KernelPCATest; class KernelPCA { public: friend class KernelPCATest; - typedef BinaryKdTreeMMAPMMKnnNode_t Tree_t; - typedef Tree_t::Precision_t Precision_t; class GaussianKernel { public: void set(double bandwidth) { @@ -50,10 +47,10 @@ class KernelPCA { ~KernelPCA() { Destruct(); } - void Init(std::string data_file, - std::string index_file); + void Init(std::string data_file, index_t knns, + index_t leaf_size); void Destruct(); - void ComputeNeighborhoods(index_t knn); + void ComputeNeighborhoods(); void LoadAffinityMatrix(); void EstimateBandwidth(double *bandwidth); static void SaveToTextFile(std::string file, @@ -69,8 +66,7 @@ class KernelPCA { Matrix *eigen_vectors, std::vector *eigen_values); void ComputeIsomap(index_t num_of_eigenvalues); - void ComputeLLE(index_t knns, - index_t num_of_eigenvalues, + void ComputeLLE(index_t num_of_eigenvalues, Matrix *eigen_vectors, std::vector *eigen_values); template @@ -79,10 +75,11 @@ class KernelPCA { void ComputeSpectralRegression(std::string label_file); private: - Tree_t tree_; + AllkNN allknn_; + index_t knns_; + Matrix data_; SparseMatrix kernel_matrix_; SparseMatrix affinity_matrix_; - BinaryDataset data_; index_t dimension_; }; diff --git a/fastlib/u/nvasil/kernel_pca/kernel_pca_impl.h b/fastlib/u/nvasil/kernel_pca/kernel_pca_impl.h index ca14d649d6..b52a123283 100644 --- a/fastlib/u/nvasil/kernel_pca/kernel_pca_impl.h +++ b/fastlib/u/nvasil/kernel_pca/kernel_pca_impl.h @@ -17,43 +17,34 @@ */ -void KernelPCA::Init(std::string data_file, - std::string index_file) { - if (index_file.empty()) { - data_.Init(data_file); - } else { - data_.Init(data_file, index_file); - } - dimension_=data_.get_dimension(); - tree_.Init(&data_); - mmapmm::MemoryManager::allocator_ = - new mmapmm::MemoryManager(); - mmapmm::MemoryManager::allocator_->Init(); +void KernelPCA::Init(std::string data_file, index_t knns, + index_t leaf_size) { + data::Load(data_file.c_str(), &data_); + knns_ = knns; + allknn_.Init(data_, data_, leaf_size, knns); } void KernelPCA::Destruct() { - tree_.Destruct(); - data_.Destruct(); - unlink("allnn.txt"); - if (mmapmm::MemoryManager::allocator_ != NULL) { - delete mmapmm::MemoryManager::allocator_; - mmapmm::MemoryManager::allocator_=NULL; - } + } -void KernelPCA::ComputeNeighborhoods(index_t knns) { +void KernelPCA::ComputeNeighborhoods() { NONFATAL("Building tree...\n"); fflush(stdout); - tree_.set_knns(knns); - tree_.BuildDepthFirst(); - NONFATAL("Memory usage: %llu\n", - (unsigned long long)Tree_t::Allocator_t::allocator_->get_usage()); - NONFATAL("Tree Statistics\n %s\n", tree_.Statistics().c_str()); - NONFATAL("Computing all nearest neighbors...\n"); - fflush(stdout); - tree_.AllNearestNeighbors(tree_.get_parent(), knns); - NONFATAL("Collecting results....\n"); - tree_.CollectKNearestNeighborWithFwriteText("allnn.txt"); + ArrayList resulting_neighbors; + ArrayList distances; + allknn_.ComputeNeighbors(&resulting_neighbors, + &distances); + FILE *fp=fopen("allnn.txt", "w"); + if (fp==NULL) { + FATAL("Unable to open allnn for exporting the results, error %s\n", + strerror(errno)); + } + for(index_t i=0; i @@ -64,7 +55,7 @@ void KernelPCA::ComputeGeneralKernelPCA(DISTANCEKERNEL kernel, kernel_matrix_.Copy(affinity_matrix_); kernel_matrix_.ApplyFunction(kernel); Vector temp; - temp.Init(kernel_matrix_.get_dimension()); + temp.Init(kernel_matrix_.dimension()); temp.SetAll(1.0); kernel_matrix_.SetDiagonal(temp); kernel_matrix_.EndLoading(); @@ -127,48 +118,46 @@ void KernelPCA::EstimateBandwidth(double *bandwidth) { *bandwidth=mean/count; } -void KernelPCA::ComputeLLE(index_t knns, - index_t num_of_eigenvalues, +void KernelPCA::ComputeLLE(index_t num_of_eigenvalues, Matrix *eigen_vectors, - std::vector *eigen_values); -{ - FILE *fp=fopen("allnn.txt"); + std::vector *eigen_values) { + FILE *fp=fopen("allnn.txt", "r"); if unlikely(fp==NULL) { FATAL("Unable to open allnn.txt, error %s\n", strerror(errno)); } uint64 p1, p2; double dist; - double mean=0; uint64 last_point=numeric_limits::max(); Vector point; point.Init(dimension_); - Matriix neighbors; - neighbor_vals.Init(dimension_, knns); - Matrix cov(neighbors); + Matrix neighbor_vals; + neighbor_vals.Init(dimension_, knns_); + Matrix cov(neighbor_vals); Vector ones; ones.Init(dimension_); ones.SetAll(1); Vector weights; - index_t neighbors[knns]; + index_t neighbors[knns_]; index_t i; - kernel_matrix_.Init(data_.get_num_of_points(), - data_.get_num_of_point()); + kernel_matrix_.Init(data_.n_rows(), + data_.n_rows(), + knns_); while (!feof(fp)) { fscanf(fp, "%llu %llu %lg", &p1, &p2, &dist); i=0; if (p1==last_point) { - memcpy(neighbor_vals.GetColumnPtr(i), data_.At(p2), + memcpy(neighbor_vals.GetColumnPtr(i), data_.GetColumnPtr(p2), sizeof(double)*dimension_); neighbors[i]=p2; la::SubFrom(dimension_, point.ptr(), neighbor_vals.GetColumnPtr(i)); } else { - point.Copy(data_.At()); + point.Copy(data_.GetColumnPtr(p1), dimension_); last_point=p1; i=0; la::MulTransBInit(neighbor_vals, neighbor_vals, &cov); la::SolveInit(cov, ones, &weights); - kernel_matrix_.LoadRow(p1, neighbors,weights.ptr()); - weights.Destruct() + kernel_matrix_.LoadRow(p1, knns_, neighbors, weights.ptr()); + weights.Destruct(); } } kernel_matrix_.Negate(); diff --git a/fastlib/u/nvasil/kernel_pca/kernel_pca_test.cc b/fastlib/u/nvasil/kernel_pca/kernel_pca_test.cc index 5ce2f660f5..b262cd5223 100644 --- a/fastlib/u/nvasil/kernel_pca/kernel_pca_test.cc +++ b/fastlib/u/nvasil/kernel_pca/kernel_pca_test.cc @@ -25,7 +25,7 @@ class KernelPCATest { public: void Init() { engine_ = new KernelPCA(); - engine_->Init("test_data_3_1000", ""); + engine_->Init("test_data_3_1000.csv", 3, 20); } void Destruct() { delete engine_; @@ -34,7 +34,7 @@ class KernelPCATest { Matrix eigen_vectors; std::vector eigen_values; Init(); - engine_->ComputeNeighborhoods(10); + engine_->ComputeNeighborhoods(); double bandwidth; engine_->EstimateBandwidth(&bandwidth); NONFATAL("Estimated bandwidth %lg ...\n", bandwidth); diff --git a/fastlib/u/nvasil/kernel_pca/test_data_3_1000 b/fastlib/u/nvasil/kernel_pca/test_data_3_1000 deleted file mode 100755 index b98eca2a2d8ec861353bc632f79794781f1663c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12004 zcmWNXcRba97{xO)%4i@N$%wLva6jiHn}!`3NfDtE5gDmpDYTUK9@Y<>fWn+H;-|hMx(qlj z8E_{RuG=T$`-cOP0nfEzp%e!T#oOY;RSg_pog@0i&qDvBz>U)~SY!T9L=G`W@WA8Z z?a69Mj~HVlyQraR{V*KJ55U9z%Y^LF1wv+uhvc5eA&D$!VcOa@p*&gwxGdO%ijvJNS{IBEHOy#*cbeINPljp8fwwqC=13mr0$-f6y!*=o#VC<}0u& z?}1%SXQ1h^7B;76if>)&Fcv2eGQ1D0mD{oF*B?YZza|#y_r~kSW`s0Z<1TDPbm&*C z**pw0n#=H}>>Ga8_l0`LdL-Ta0qq0NMeDbxnD(m;tr3R!x6cC$18yS<%6PSW1^!jN z!HZ$z5jA^`(5cymbRSy`(h0$W%kQxIf|u~ycnTvUoAI-0y(Do#qX=Gj0yB?fhzh#{ z;^oT{^f6r~jy>{1PSh#98aWZWJIi77pk~PINs*j5_ycF{Z$NQ*EC&8gMc;8x5p%Ux zbSb@qihw!5wXgp z;_2Muxbk|52-`3iEhlb5w&w_VUDXtqzpcbF$3eK-#TzcQYtX2mf-O~J@bpd@^5dPv z<-v8rTImARMyo*kb`}x~jw0rq8q80oOFG>uL0HduqVfr`Jn%n!+HWK5Z`8RZ`BcEH z{G1q4Z~=kmcj4J4eTm}B?HIHFEwtLbMAh*(uur{=VOg4xci)52Nq5n2ggv4%FJPrZ zB|00Vh>Puuu{q)r`m3E2|EVlNfOQjYTYeYG>z?BLe_wFbULHpdeMd}iDORn`!qzT# z(Rq3U?xz-^EPX#t>O9B4@fK)`nSog=R$yOyPXq+4!Q9++cu|vzV8t7dEonudk~xP&4E^u1BC^TIk!~Mx`I!znr0u|i#}Dx5Ril`6r~)}%E@Aj#Rk6Zs4SwjpgXh_f z_*u6MGs7++qn92EJ(l3bh&GJKbrpkeTtaLA`MCXLEN*t&hdv4OaC+!*aXMr!$|`Ho z-6%wotT6-UT^7MEC;>8S7NRdc;QNmM#MHI#F{T#A>A#nB}K%Mt3)~pRB{YH%Cx=dIn~j&BN@HMVPnp02++`;m`PosHoVA_@C=L z^)oK@Zo-D?2VtsFj${3QA*t*tTtnU=C;Jo%w4Y*A$afeguffKI9Z+6ghw49SG?_63 zRM{syC5qg�I#ifIW|`X=`#^yg9BRY%eSD1?2HBE89)}oeW1f-WCs2+a&QrO<2(< z5Dzrxim$`%>9t!6hi;{a$c9dQ*kmQ1%}T>^GgF>B)*WZ3M&ioOo^%}G2=5c}Zl{fV z^Iw9wcvny_so(F)oSst!|Fw#Hp~_6N?*nv>z~>J7Ja1`+E(w1n3AWx09i@%yP1;a2 z7|4SzGVqJH6(ui&XrYlJA2WFU4GMb$_T@kCLGhseG`R|&G@abxS8Xr~8)(?lLD1myW5mT<~;j-acd<(SZ zGP{QoNAFF@jW_0A_X#fDZ|^`;q8{t(7K+GM>!AMIkWD?~g~#(tNU?HZYyM;$yYv&A zJykfsGy;yHtDsb^!q9_{-Hvu|g?@f-{(0eokjzU+T;oKg8Og|Zeu2x)gLu(g1z6U4Ld~Z4*j~b(lS5GJ}`*g2eI$mOoFXio6LLm()WH3t-`e zIby@*H3*T3yvX7ul7v z%-gm{TI;)n&3~p`S@jHwSM8DfS)bny&cVsAqj25Xm>Vjm;dnz=+#jUDY1e+D<)#6w zT^+gY!cy4G-YO=3vEh>b)sR!@3)6C4I!Bk{M@0a(A9JHdW*u6t7)hur;g~6Z;s0@@ zc(vbz17$YjkNj=P8EZ!#xV#DjkIWEq`i|^7_uF@;)~6j$8+E4MwI6i3b#|<%rE_kV=;iFQy{Hki*as92sS8s@n?K3 z7L=5VJ}<{J^WuLP+9e3GMTI=K?FrOWo7`q6P2{5NR%E<##7MnNb{kd?Tlqw>Zuk%; z?0A4xKe8~cFrDeUK4D_PLP@`SUL4}M44H;b=+$=upC%Ne-qZx4;lA8|vkh8z({Lwd z2#d@=qT{+@c)KN&J##l;<@oJxHmz<{iob?))lE3Ed@4U~T?@Ysr?BN+KDY8BPLAJ= zNmZ(ht5}FTca9<8R6pvC_<`;%r?JyyJU8rFgM5!w=LWZ_7vNtewPfzP0eM z*^2*~(^>HDF%C-S$Dyp=-1_e*`oFx7#M1G+yrc~-kvHLd+m;Q+(lbZD#-#QrTIjq- zd09E^0w>UC(rWa|e~Zc&y*b1GqO?ZZp;nm3u&8OU>+~I4HtO(t>Q=nC@de7KvN>tn zSB$Tyh0?W2H1}=A_U3IUSJk51p+opL@dQeA)Hx%z9bq#rVDS$*?)kMH?>9d{ewaJk zW7Z+n<{i8ij-l4|QW&OBNA_WV@vN&RtM;{vz0c#}yuyV~&Xfwdh9Oux)Re>b{t*d3 zrsGMeA|qa^<4xoQQE^Y1a}N%}n8UAxmxCGmn7k38v2JM1mf^qiIbu@sB(XkBm)l+p z5vngek?8D9n}})fT$_nmOM25*Hw_la1L1Y6C;!ETk_ZrMWD7nIxLpu-M8Twv3{ADc36ipi##yTC?6Nq`tiy@Eo>d6C3&xE zN~az#MD3X;;(WXzU)OcSsPOUPLX!$#hrYt^6-m&xkmvE`uTj8UNu(9IyQKxUx?L4d z7VGe~{s&Ad&_(Vxd-gnX5_QThh&!UmIZxXVx4u{smj|v2ufUL!@j@?Ei^Zxpkm7wt zm~}B`uk%ll&}WoznBRlr{Tre7%nbQk$kLf7pz&M|EzLH}w_OJHVb6t* z(?_vC_YlHUI&qugECffshFrQb$Mu~pv_^lzmuJ?TP^g5x;f=5#ug!zvt61{zF5X4< z=gkxIg<5qLf($fxd+KK~b9x)f#`fT1voLHic#NNg+PpO5waC07)ln+?EH6HRvD@c^ zUlr-}V=ZRCxrPTT!Fby_kXdvDzM=zLEw*9)*$&*_3!;~o&aXOUIyDum5B3KWkTC3Jbd1SZH*30owEu0 z#`A=#RxBMKEGA*^L70M9A+<;$^cB zZd(L$$1s(XdRIstsgpw-0bivV=llAanP(VXx&Iaictf183UeY_s3@N_5v$5__)XQL_0fPMb^pPoKfG(SC_>J?&6^C6~s(O7Ox&Q*1h$ z%N^B!VGw4EUQ+Uo#P!h7IRa~%7-?3G-y-6m$}22*8=7Y==!E4DYX2B4la}wx$D8(J&c#KJ>m6s0!ki@r-NUS z=x%)*5qep?uxODe@hQfzCxsl@H4<@Z&1k)x$lUe=Zh;-^P}CUAQU!`WcCRsW*_6Yl{KpOqlpjOyyBYYPb`8$;V>rPuO^mC3i!B(- z|GMp$91T5#GQC8)Ta<|}F0HuUlF8v!TIju_2D|$OGGL*RWJ~AE_`P#5i;|6zQTqdm z9LojcDkYcqbYPj2J4ap1K|+$c4S+SdTGozf!C@Hlr`6e^y4=WqlSa8gbKON9?iH z;k*UCI$LA*T{qVB*X20tWO1b0j1hXS%(ybq&7fR|uBN{1y-Znb z+2G9Y52HA}vy@Gpm1FasEWSGXO%%x+bK|abYAg2<&j-1(&UhTRh20Tlvc{bD)RXIn z#fY$fdi;^*%i8Tj-5fM6*)}hnom$pO4y?9jQtK!>&e|e=Z0pP{H(wq*G+&I|qCwxD zQ#gJ4DM@IgBb#i8aEpBi*14E*%*!yAD=d^Ol~bpGoG*u)YQWo6j$6;?(=}fLpRQK) ztjpk^FD7E&xzhM0IJlQ$;oaAeUNiQ!;>-kB=LixDF*USTkMNWGu#X(xw2n?3hNUBF&l6JGgTK*clvaIkd%k5(1%-}1v4dsvCuCa(02S_9)9s@%RSkUeXD z;Bihj*6zyX*1qr2XRI7oj_S${nu}1_PlM~iGU(B^0d`xh+3~X>&u7+RRhr

L5NZ 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