Repository navigation
Fix L2 expanded distance clamp for cutlass and legacy fused backend #2841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3206b85
b49edb5
b06f50f
e04a564
b75a23d
4339940
a5ced2d
d61603a
9bc2785
7b4be11
855d1f8
2e76070
0fb806d
f3aefcb
022fd6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,17 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2018-2024, NVIDIA CORPORATION. | ||
| * SPDX-FileCopyrightText: Copyright (c) 2018-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include "../test_utils.cuh" | ||
| #include "distance_base.cuh" | ||
|
|
||
| #include <raft/core/device_mdarray.hpp> | ||
| #include <raft/util/cudart_utils.hpp> | ||
|
|
||
| #include <cmath> | ||
| #include <vector> | ||
|
|
||
| namespace cuvs { | ||
| namespace distance { | ||
|
|
||
|
|
@@ -162,5 +168,92 @@ INSTANTIATE_TEST_CASE_P(DistanceTests, DistanceEucExpTestD, ::testing::ValuesIn( | |
|
|
||
| class BigMatrixEucExp : public BigMatrixDistanceTest<cuvs::distance::DistanceType::L2Expanded> {}; | ||
| TEST_F(BigMatrixEucExp, Result) {} | ||
|
|
||
| // Distinct points with equal norms must keep their (small) distances. The clamp of self-distance | ||
| // round-off to zero used to apply an absolute tolerance, which collapsed such points when the data | ||
| // has a small magnitude (e.g. a spectral embedding). | ||
| template <typename T> | ||
| void test_small_scale_equal_norms(T scale) | ||
| { | ||
| raft::resources handle; | ||
| auto stream = raft::resource::get_cuda_stream(handle); | ||
| constexpr int64_t n = 8; | ||
| std::vector<T> h_x(n * n, T{0}); | ||
| for (int64_t i = 0; i < n; i++) { | ||
| h_x[i * n + i] = scale; | ||
| } | ||
| auto x = raft::make_device_matrix<T, int64_t>(handle, n, n); | ||
| auto d = raft::make_device_matrix<T, int64_t>(handle, n, n); | ||
| raft::update_device(x.data_handle(), h_x.data(), h_x.size(), stream); | ||
|
|
||
| for (auto metric : {DistanceType::L2Expanded, DistanceType::L2SqrtExpanded}) { | ||
| cuvs::distance::pairwise_distance(handle, | ||
| raft::make_const_mdspan(x.view()), | ||
| raft::make_const_mdspan(x.view()), | ||
| d.view(), | ||
| metric); | ||
| std::vector<T> h_d(n * n); | ||
| raft::update_host(h_d.data(), d.data_handle(), h_d.size(), stream); | ||
| raft::resource::sync_stream(handle, stream); | ||
|
|
||
| const T expected = | ||
| metric == DistanceType::L2Expanded ? T(2) * scale * scale : std::sqrt(T(2)) * scale; | ||
| for (int64_t i = 0; i < n; i++) { | ||
| for (int64_t j = 0; j < n; j++) { | ||
| EXPECT_NEAR(h_d[i * n + j], i == j ? T(0) : expected, T(1e-3) * expected) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we add a focused regression for the new exact-zero diagonal guarantee, using dense rows and EXPECT_EQ on the diagonal for both row-major and column-major self-comparisons? This test catches the original positive-distance collapse, but it doesn't specifically protect the new diagonal pass, right?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the review! I added all the new tests here f3aefcb |
||
| << "metric=" << static_cast<int>(metric) << " i=" << i << " j=" << j; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| TEST(DistanceEucExpSmallScale, EqualNormsF) { test_small_scale_equal_norms<float>(1e-2f); } | ||
| TEST(DistanceEucExpSmallScale, EqualNormsD) { test_small_scale_equal_norms<double>(1e-5); } | ||
|
|
||
| template <typename Layout> | ||
| void test_exact_self_distance_diagonal() | ||
| { | ||
| raft::resources handle; | ||
| auto stream = raft::resource::get_cuda_stream(handle); | ||
| constexpr int64_t n_rows = 50; | ||
| constexpr int64_t n_cols = 10; | ||
| constexpr bool row_major = std::is_same_v<Layout, raft::row_major>; | ||
| std::vector<float> h_x(n_rows * n_cols); | ||
| for (int64_t i = 0; i < n_rows; ++i) { | ||
| for (int64_t j = 0; j < n_cols; ++j) { | ||
| auto index = row_major ? i * n_cols + j : j * n_rows + i; | ||
| h_x[index] = static_cast<float>(((i + 3) * (j + 5)) % 37) / 37.0f; | ||
| } | ||
| } | ||
|
|
||
| auto x = raft::make_device_matrix<float, int64_t, Layout>(handle, n_rows, n_cols); | ||
| auto d = raft::make_device_matrix<float, int64_t, Layout>(handle, n_rows, n_rows); | ||
| raft::update_device(x.data_handle(), h_x.data(), h_x.size(), stream); | ||
|
|
||
| for (auto metric : {DistanceType::L2Expanded, DistanceType::L2SqrtExpanded}) { | ||
| cuvs::distance::pairwise_distance(handle, | ||
| raft::make_const_mdspan(x.view()), | ||
| raft::make_const_mdspan(x.view()), | ||
| d.view(), | ||
| metric); | ||
| std::vector<float> h_d(n_rows * n_rows); | ||
| raft::update_host(h_d.data(), d.data_handle(), h_d.size(), stream); | ||
| raft::resource::sync_stream(handle, stream); | ||
|
|
||
| for (int64_t i = 0; i < n_rows; ++i) { | ||
| EXPECT_EQ(h_d[i * n_rows + i], 0.0f) << "metric=" << static_cast<int>(metric) << " row=" << i; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| TEST(DistanceEucExpSelfDistance, ExactDiagonalRowMajor) | ||
| { | ||
| test_exact_self_distance_diagonal<raft::row_major>(); | ||
| } | ||
|
|
||
| TEST(DistanceEucExpSelfDistance, ExactDiagonalColMajor) | ||
| { | ||
| test_exact_self_distance_diagonal<raft::col_major>(); | ||
| } | ||
| } // end namespace distance | ||
| } // namespace cuvs | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to use alternative primitives like raft::matrix::set_diagonal, but that requires a temporary allocation and copy, whereas we should just be able to write in place. I also looked into raft::map, raft::map_offset, and raft::unary_op but they require that we write to every element of out.