@@ -288,3 +288,116 @@ TEST_CASE(
288288 // Verify results for kmeans_pp
289289 verify_centroids (centroids_pp);
290290}
291+
292+ TEST_CASE (" test kmeans train_no_init random data" , " [kmeans]" ) {
293+ // Sample data: 6-dimensional vectors, 10 vectors total (column major)
294+ std::vector<float > data = {
295+ 7 , 6 , 249 , 3 , 2 , 2 , 254 , 249 , 7 , 0 , 9 , 3 , 248 , 255 , 4 ,
296+ 0 , 249 , 0 , 251 , 249 , 245 , 3 , 250 , 252 , 6 , 7 , 5 , 252 , 4 , 5 ,
297+ 9 , 9 , 248 , 254 , 7 , 1 , 4 , 1 , 253 , 5 , 2 , 255 , 250 , 6 , 3 ,
298+ 0 , 2 , 249 , 0 , 250 , 5 , 4 , 5 , 2 , 99 , 30 , 3 , 1 , 55 , 88 };
299+
300+ ColMajorMatrix<float > training_set (6 , 10 ); // 6 rows, 10 columns
301+ std::copy (data.begin (), data.end (), training_set.data ());
302+
303+ // Initial centroids: 6-dimensional vectors, 3 centroids total
304+ std::vector<float > centroids_data = {
305+ 3 ,
306+ 5 ,
307+ 250 ,
308+ 245 ,
309+ 249 ,
310+ 0 ,
311+ 249 ,
312+ 248 ,
313+ 250 ,
314+ 0 ,
315+ 5 ,
316+ 251 ,
317+ 251 ,
318+ 249 ,
319+ 245 ,
320+ 3 ,
321+ 250 ,
322+ 252 };
323+
324+ ColMajorMatrix<float > centroids (6 , 3 );
325+ std::copy (centroids_data.begin (), centroids_data.end (), centroids.data ());
326+
327+ size_t dimension_ = 6 ;
328+ size_t num_partitions_ = 3 ;
329+ uint32_t max_iterations = 2 ;
330+ float tol_ = 2.5e-05 ;
331+ size_t num_threads_ = 12 ;
332+ float reassign_ratio_ = 0.075 ;
333+
334+ CHECK (centroids.num_rows () == dimension_);
335+ CHECK (centroids.num_cols () == num_partitions_);
336+
337+ train_no_init (
338+ training_set,
339+ centroids,
340+ dimension_,
341+ num_partitions_,
342+ max_iterations,
343+ tol_,
344+ num_threads_,
345+ reassign_ratio_);
346+
347+ CHECK (centroids.num_rows () == dimension_);
348+ CHECK (centroids.num_cols () == num_partitions_);
349+
350+ {
351+ ColMajorMatrix<float > original_centroids (6 , 3 );
352+ std::copy (
353+ centroids_data.begin (),
354+ centroids_data.end (),
355+ original_centroids.data ());
356+ float max_diff = 0.0 ;
357+ for (size_t i = 0 ; i < centroids.num_cols (); ++i) {
358+ float diff =
359+ sum_of_squares_distance{}(centroids[i], original_centroids[i]);
360+ max_diff = std::max (max_diff, diff);
361+ }
362+ REQUIRE_THAT (max_diff, Catch::Matchers::WithinAbs (91858 .75f , 1e-2 ));
363+ }
364+ }
365+
366+ TEST_CASE (" test kmeans train_no_init training_set is empty" , " [kmeans]" ) {
367+ ColMajorMatrix<float > training_set (0 , 0 ); // Empty training set
368+ ColMajorMatrix<float > centroids (0 , 0 ); // Empty centroids
369+
370+ train_no_init (training_set, centroids, 0 , 0 , 2 , 0.00001 , 12 , 0.075 );
371+
372+ CHECK (centroids.num_cols () == 0 ); // Expect centroids to remain empty
373+ CHECK (centroids.num_rows () == 0 );
374+ }
375+
376+ TEST_CASE (
377+ " test kmeans train_no_init number of centroids exceeds data points" ,
378+ " [kmeans]" ) {
379+ std::vector<float > small_data = {1 , 2 , 3 , 4 , 5 , 6 };
380+ ColMajorMatrix<float > small_training_set (6 , 1 ); // 6 rows, 1 column
381+ std::copy (small_data.begin (), small_data.end (), small_training_set.data ());
382+
383+ ColMajorMatrix<float > more_centroids (
384+ 6 , 3 ); // More centroids than data points
385+
386+ train_no_init (
387+ small_training_set, more_centroids, 6 , 3 , 2 , 0.00001 , 12 , 0.075 );
388+
389+ CHECK (more_centroids.num_cols () == 3 ); // Verify centroids were generated
390+ for (size_t i = 0 ; i < more_centroids.num_cols (); ++i) {
391+ // Ensure some centroids match the data point and the rest are zeros
392+ bool is_zero = std::all_of (
393+ more_centroids[i].begin (), more_centroids[i].end (), [](float val) {
394+ return val == 0 .0f ;
395+ });
396+ if (!is_zero) {
397+ CHECK (std::equal (
398+ more_centroids[i].begin (),
399+ more_centroids[i].end (),
400+ small_data.begin ()));
401+ }
402+ }
403+ }
0 commit comments