Skip to content

Commit 4f58b38

Browse files
authored
Fix types in qv_partition_with_scores() and train_no_init() (#546)
1 parent 001b772 commit 4f58b38

3 files changed

Lines changed: 121 additions & 11 deletions

File tree

src/include/detail/flat/qv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ auto qv_partition_with_scores(
536536

537537
// Just need a single vector
538538
std::vector<size_t> top_k(q.num_cols());
539-
std::vector<size_t> top_k_scores(q.num_cols());
539+
std::vector<float> top_k_scores(q.num_cols());
540540

541541
auto par = stdx::execution::indexed_parallel_policy{(size_t)nthreads};
542542
stdx::range_for_each(

src/include/index/kmeans.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,6 @@ void train_no_init(
254254
if (::num_vectors(training_set) == 0) {
255255
return;
256256
}
257-
using feature_type = typename V::value_type;
258257
using centroid_feature_type = typename C::value_type;
259258
using index_type = size_t;
260259

@@ -276,10 +275,9 @@ void train_no_init(
276275
// How many centroids should we try to fix up
277276
size_t heap_size =
278277
std::ceil(reassign_ratio_ * static_cast<float>(num_partitions_)) + 5;
279-
auto high_scores = fixed_min_pair_heap<
280-
feature_type,
281-
index_type,
282-
std::greater<feature_type>>(heap_size, std::greater<feature_type>());
278+
auto high_scores =
279+
fixed_min_pair_heap<float, index_type, std::greater<float>>(
280+
heap_size, std::greater<float>());
283281
auto low_degrees = fixed_min_pair_heap<index_type, index_type>(heap_size);
284282

285283
// @todo parallelize -- by partition
@@ -326,7 +324,7 @@ void train_no_init(
326324
std::sort_heap(begin(high_scores), end(high_scores), [](auto a, auto b) {
327325
return std::get<0>(a) > std::get<0>(b);
328326
});
329-
for (size_t i = 0; i < size(low_degrees) &&
327+
for (size_t i = 0; i < std::min(size(low_degrees), size(high_scores)) &&
330328
std::get<0>(low_degrees[i]) <= lower_degree_bound;
331329
++i) {
332330
// std::cout << "i: " << i << " low_degrees: ("
@@ -527,10 +525,9 @@ auto sub_kmeans(
527525
#ifdef REASSIGN
528526
// How many centroids should we try to fix up
529527
size_t heap_size = std::ceil(reassign_ratio * num_clusters) + 5;
530-
auto high_scores = fixed_min_pair_heap<
531-
feature_type,
532-
index_type,
533-
std::greater<feature_type>>(heap_size, std::greater<feature_type>());
528+
auto high_scores =
529+
fixed_min_pair_heap<float, index_type, std::greater<float>>(
530+
heap_size, std::greater<float>());
534531
auto low_degrees = fixed_min_pair_heap<index_type, index_type>(heap_size);
535532
#endif
536533

src/include/test/unit_kmeans.cc

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)