Skip to content

Commit be4cb4a

Browse files
committed
make vq codebook optional
1 parent 7247688 commit be4cb4a

6 files changed

Lines changed: 90 additions & 55 deletions

File tree

c/src/preprocessing/quantize/pq.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,12 @@ extern "C" cuvsError_t cuvsProductQuantizerGetVqCodebook(cuvsProductQuantizer_t
272272
if (quantizer != nullptr) {
273273
auto quant_addr = quantizer->addr;
274274
if (quantizer->dtype.code == kDLFloat && quantizer->dtype.bits == 32) {
275-
auto pq_mdspan =
275+
auto vq_opt =
276276
(reinterpret_cast<cuvs::preprocessing::quantize::pq::quantizer<float>*>(quant_addr))
277277
->codebooks.vq_code_book();
278-
cuvs::core::to_dlpack(pq_mdspan, vq_codebook);
278+
RAFT_EXPECTS(vq_opt.has_value(),
279+
"quantizer has no VQ codebook (build with use_vq=true to enable)");
280+
cuvs::core::to_dlpack(vq_opt.value(), vq_codebook);
279281
} else {
280282
RAFT_FAIL("Unsupported quantizer dtype: %d and bits: %d",
281283
quantizer->dtype.code,

cpp/include/cuvs/preprocessing/quantize/vpq_dataset.hpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include <cuvs/neighbors/common.hpp>
99

10+
#include <optional>
11+
1012
namespace cuvs::preprocessing::quantize::pq {
1113

1214
/**
@@ -21,18 +23,29 @@ class vpq_codebooks_iface {
2123

2224
virtual ~vpq_codebooks_iface() = default;
2325

24-
/** VQ codebook [vq_n_centers, dim]. */
26+
/**
27+
* VQ codebook [vq_n_centers, dim].
28+
*
29+
* Returns std::nullopt when no VQ codebook is configured (i.e. PQ-only,
30+
* use_vq=false). Callers that need to forward a `device_matrix_view`
31+
* downstream should materialize an empty 0x0 view themselves on nullopt.
32+
*/
2533
[[nodiscard]] virtual auto vq_code_book() const noexcept
26-
-> raft::device_matrix_view<const math_type, uint32_t, raft::row_major> = 0;
34+
-> std::optional<raft::device_matrix_view<const math_type, uint32_t, raft::row_major>> = 0;
2735

2836
/** PQ codebook [pq_n_centers (× pq_dim for subspaces), pq_len]. */
2937
[[nodiscard]] virtual auto pq_code_book() const noexcept
3038
-> raft::device_matrix_view<const math_type, uint32_t, raft::row_major> = 0;
3139

32-
[[nodiscard]] virtual auto dim() const noexcept -> uint32_t { return vq_code_book().extent(1); }
40+
[[nodiscard]] virtual auto dim() const noexcept -> uint32_t
41+
{
42+
auto vq = vq_code_book();
43+
return vq.has_value() ? vq->extent(1) : 0;
44+
}
3345
[[nodiscard]] virtual auto vq_n_centers() const noexcept -> uint32_t
3446
{
35-
return vq_code_book().extent(0);
47+
auto vq = vq_code_book();
48+
return vq.has_value() ? vq->extent(0) : 0;
3649
}
3750
[[nodiscard]] virtual auto pq_len() const noexcept -> uint32_t
3851
{
@@ -89,8 +102,11 @@ class vpq_codebooks {
89102
vpq_codebooks& operator=(vpq_codebooks&&) = default;
90103
~vpq_codebooks() = default;
91104

105+
/**
106+
* VQ codebook view, or std::nullopt when no VQ codebook is configured.
107+
*/
92108
[[nodiscard]] auto vq_code_book() const noexcept
93-
-> raft::device_matrix_view<const math_type, uint32_t, raft::row_major>
109+
-> std::optional<raft::device_matrix_view<const math_type, uint32_t, raft::row_major>>
94110
{
95111
return impl_->vq_code_book();
96112
}
@@ -150,8 +166,11 @@ class vpq_dataset : public cuvs::neighbors::dataset<IdxT> {
150166
[[nodiscard]] uint32_t dim() const noexcept override { return codebooks.dim(); }
151167
[[nodiscard]] bool is_owning() const noexcept override { return true; }
152168

169+
/**
170+
* VQ codebook view, or std::nullopt when no VQ codebook is configured.
171+
*/
153172
[[nodiscard]] auto vq_code_book() const noexcept
154-
-> raft::device_matrix_view<const math_type, uint32_t, raft::row_major>
173+
-> std::optional<raft::device_matrix_view<const math_type, uint32_t, raft::row_major>>
155174
{
156175
return codebooks.vq_code_book();
157176
}

cpp/src/neighbors/detail/cagra/compute_distance_vpq.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ struct vpq_descriptor_spec : public instance_spec<DataT, IndexT, DistanceT> {
5050
cuvs::distance::DistanceType metric,
5151
const DistanceT* dataset_norms = nullptr) -> host_type
5252
{
53+
auto vq_view = dataset.vq_code_book();
5354
return init_(params,
5455
dataset.data.data_handle(),
5556
dataset.encoded_row_length(),
56-
dataset.vq_code_book().data_handle(),
57+
vq_view.has_value() ? vq_view->data_handle() : nullptr,
5758
dataset.pq_code_book().data_handle(),
5859
IndexT(dataset.n_rows()),
5960
dataset.dim());

cpp/src/neighbors/detail/dataset_serialize.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ void serialize(const raft::resources& res,
6969
raft::serialize_scalar(res, os, dataset.pq_n_centers());
7070
raft::serialize_scalar(res, os, dataset.pq_len());
7171
raft::serialize_scalar(res, os, dataset.encoded_row_length());
72-
raft::serialize_mdspan(res, os, dataset.vq_code_book());
72+
auto vq_view = dataset.vq_code_book().value_or(
73+
raft::make_device_matrix_view<const MathT, uint32_t, raft::row_major>(nullptr, 0, 0));
74+
raft::serialize_mdspan(res, os, vq_view);
7375
raft::serialize_mdspan(res, os, dataset.pq_code_book());
7476
raft::serialize_mdspan(res, os, dataset.data.view());
7577
}
@@ -160,10 +162,12 @@ auto deserialize_vpq(raft::resources const& res, std::istream& is)
160162
raft::deserialize_mdspan(res, is, pq_code_book.view());
161163
raft::deserialize_mdspan(res, is, data.view());
162164

165+
std::optional<raft::device_matrix<MathT, uint32_t, raft::row_major>> vq_code_book_opt;
166+
if (vq_n_centers > 0) { vq_code_book_opt = std::move(vq_code_book); }
163167
return std::make_unique<cuvs::preprocessing::quantize::pq::vpq_dataset<MathT, IdxT>>(
164168
cuvs::preprocessing::quantize::pq::vpq_codebooks<MathT>{
165169
std::make_unique<cuvs::preprocessing::quantize::pq::vpq_codebooks_owning<MathT>>(
166-
std::move(vq_code_book), std::move(pq_code_book))},
170+
std::move(pq_code_book), std::move(vq_code_book_opt))},
167171
std::move(data));
168172
}
169173

cpp/src/preprocessing/quantize/detail/pq.cuh

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,11 @@ quantizer<MathT> build(
154154
pq_code_book = cuvs::neighbors::detail::train_pq<MathT>(
155155
res, vpq_params, dataset, raft::make_const_mdspan(vq_code_book.view()));
156156
}
157+
std::optional<raft::device_matrix<MathT, uint32_t, raft::row_major>> vq_code_book_opt;
158+
if (filled_params.use_vq) { vq_code_book_opt = std::move(vq_code_book); }
157159
return {filled_params,
158160
vpq_codebooks<MathT>{std::make_unique<vpq_codebooks_owning<MathT>>(
159-
std::move(vq_code_book), std::move(pq_code_book))}};
161+
std::move(pq_code_book), std::move(vq_code_book_opt))}};
160162
}
161163

162164
template <typename MathT>
@@ -195,12 +197,9 @@ quantizer<MathT> build_view(
195197
vq_centers.value().extent(0));
196198
}
197199

198-
auto vq_view =
199-
vq_centers.has_value()
200-
? vq_centers.value()
201-
: raft::make_device_matrix_view<const MathT, uint32_t, raft::row_major>(nullptr, 0, 0);
202-
return {params,
203-
vpq_codebooks<MathT>{std::make_unique<vpq_codebooks_view<MathT>>(vq_view, pq_centers)}};
200+
return {
201+
params,
202+
vpq_codebooks<MathT>{std::make_unique<vpq_codebooks_view<MathT>>(pq_centers, vq_centers)}};
204203
}
205204

206205
template <typename T, typename QuantI, typename AccessorType>
@@ -223,7 +222,9 @@ void transform(
223222
RAFT_EXPECTS(quantizer.params_quantizer.pq_bits >= 4 && quantizer.params_quantizer.pq_bits <= 16,
224223
"PQ bits must be within [4, 16]");
225224

226-
auto vq_centers = quantizer.codebooks.vq_code_book();
225+
auto vq_centers_opt = quantizer.codebooks.vq_code_book();
226+
auto vq_centers = vq_centers_opt.value_or(
227+
raft::make_device_matrix_view<const T, uint32_t, raft::row_major>(nullptr, 0, 0));
227228
auto pq_centers = quantizer.codebooks.pq_code_book();
228229
auto vq_labels_view = raft::make_device_vector_view<uint32_t, int64_t>(nullptr, 0);
229230
if (vq_labels.has_value()) { vq_labels_view = vq_labels.value(); }
@@ -360,11 +361,14 @@ void inverse_transform(
360361
RAFT_EXPECTS(quantizer.params_quantizer.pq_bits >= 4 && quantizer.params_quantizer.pq_bits <= 16,
361362
"PQ bits must be within [4, 16]");
362363

364+
auto vq_centers_opt = quantizer.codebooks.vq_code_book();
365+
auto vq_centers = vq_centers_opt.value_or(
366+
raft::make_device_matrix_view<const T, uint32_t, raft::row_major>(nullptr, 0, 0));
363367
reconstruct_vectors<T, T, idx_t, label_t>(res,
364368
quantizer.params_quantizer,
365369
codes,
366370
quantizer.codebooks.pq_code_book(),
367-
quantizer.codebooks.vq_code_book(),
371+
vq_centers,
368372
vq_labels,
369373
out,
370374
quantizer.params_quantizer.use_subspaces);
@@ -374,21 +378,26 @@ template <typename NewMathT, typename OldMathT>
374378
auto vpq_convert_math_type(const raft::resources& res, const vpq_codebooks<OldMathT>& src)
375379
-> vpq_codebooks<NewMathT>
376380
{
377-
auto vq_src = src.vq_code_book();
378-
auto pq_src = src.pq_code_book();
381+
auto vq_src_opt = src.vq_code_book();
382+
auto pq_src = src.pq_code_book();
379383

380-
auto vq_new = raft::make_device_matrix<NewMathT, uint32_t, raft::row_major>(
381-
res, vq_src.extent(0), vq_src.extent(1));
382384
auto pq_new = raft::make_device_matrix<NewMathT, uint32_t, raft::row_major>(
383385
res, pq_src.extent(0), pq_src.extent(1));
384-
385-
raft::linalg::map(
386-
res, vq_new.view(), cuvs::spatial::knn::detail::utils::mapping<NewMathT>{}, vq_src);
387386
raft::linalg::map(
388387
res, pq_new.view(), cuvs::spatial::knn::detail::utils::mapping<NewMathT>{}, pq_src);
389388

389+
std::optional<raft::device_matrix<NewMathT, uint32_t, raft::row_major>> vq_new_opt;
390+
if (vq_src_opt.has_value()) {
391+
auto vq_src = vq_src_opt.value();
392+
auto vq_new = raft::make_device_matrix<NewMathT, uint32_t, raft::row_major>(
393+
res, vq_src.extent(0), vq_src.extent(1));
394+
raft::linalg::map(
395+
res, vq_new.view(), cuvs::spatial::knn::detail::utils::mapping<NewMathT>{}, vq_src);
396+
vq_new_opt = std::move(vq_new);
397+
}
398+
390399
return vpq_codebooks<NewMathT>{
391-
std::make_unique<vpq_codebooks_owning<NewMathT>>(std::move(vq_new), std::move(pq_new))};
400+
std::make_unique<vpq_codebooks_owning<NewMathT>>(std::move(pq_new), std::move(vq_new_opt))};
392401
}
393402

394403
template <typename DatasetT, typename MathT, typename IdxT>
@@ -419,8 +428,8 @@ auto vpq_build(const raft::resources& res,
419428
true);
420429

421430
return vpq_dataset<MathT, IdxT>{
422-
vpq_codebooks<MathT>{std::make_unique<vpq_codebooks_owning<MathT>>(std::move(vq_code_book),
423-
std::move(pq_code_book))},
431+
vpq_codebooks<MathT>{std::make_unique<vpq_codebooks_owning<MathT>>(std::move(pq_code_book),
432+
std::move(vq_code_book))},
424433
std::move(codes)};
425434
}
426435

cpp/src/preprocessing/quantize/detail/vpq_dataset_impl.hpp

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@
77

88
#include <cuvs/preprocessing/quantize/vpq_dataset.hpp>
99

10+
#include <optional>
11+
1012
namespace cuvs::preprocessing::quantize::pq {
1113

1214
template <typename MathT>
1315
class vpq_codebooks_owning : public vpq_codebooks_iface<MathT> {
1416
public:
15-
using math_type = MathT;
16-
17-
vpq_codebooks_owning(raft::device_matrix<math_type, uint32_t, raft::row_major>&& vq_code_book,
18-
raft::device_matrix<math_type, uint32_t, raft::row_major>&& pq_code_book)
19-
: vq_code_book_{std::move(vq_code_book)}, pq_code_book_{std::move(pq_code_book)}
17+
using math_type = MathT;
18+
using matrix_type = raft::device_matrix<math_type, uint32_t, raft::row_major>;
19+
20+
// PQ codebook is required; VQ codebook is optional and defaults to absent
21+
// When VQ is not provided, vq_code_book() returns std::nullopt.
22+
explicit vpq_codebooks_owning(matrix_type&& pq_code_book,
23+
std::optional<matrix_type>&& vq_code_book = std::nullopt)
24+
: pq_code_book_{std::move(pq_code_book)}, vq_code_book_{std::move(vq_code_book)}
2025
{
2126
}
2227

@@ -27,15 +32,10 @@ class vpq_codebooks_owning : public vpq_codebooks_iface<MathT> {
2732
~vpq_codebooks_owning() override = default;
2833

2934
[[nodiscard]] auto vq_code_book() const noexcept
30-
-> raft::device_matrix_view<const math_type, uint32_t, raft::row_major> override
31-
{
32-
return vq_code_book_.view();
33-
}
34-
35-
[[nodiscard]] auto vq_code_book() noexcept
36-
-> raft::device_matrix_view<math_type, uint32_t, raft::row_major>
35+
-> std::optional<raft::device_matrix_view<const math_type, uint32_t, raft::row_major>> override
3736
{
38-
return vq_code_book_.view();
37+
if (!vq_code_book_.has_value()) { return std::nullopt; }
38+
return vq_code_book_.value().view();
3939
}
4040

4141
[[nodiscard]] auto pq_code_book() const noexcept
@@ -51,19 +51,21 @@ class vpq_codebooks_owning : public vpq_codebooks_iface<MathT> {
5151
}
5252

5353
private:
54-
raft::device_matrix<math_type, uint32_t, raft::row_major> vq_code_book_;
55-
raft::device_matrix<math_type, uint32_t, raft::row_major> pq_code_book_;
54+
matrix_type pq_code_book_;
55+
std::optional<matrix_type> vq_code_book_;
5656
};
5757

5858
template <typename MathT>
5959
class vpq_codebooks_view : public vpq_codebooks_iface<MathT> {
6060
public:
6161
using math_type = MathT;
62+
using view_type = raft::device_matrix_view<const math_type, uint32_t, raft::row_major>;
6263

63-
vpq_codebooks_view(
64-
raft::device_matrix_view<const math_type, uint32_t, raft::row_major> vq_code_book_view,
65-
raft::device_matrix_view<const math_type, uint32_t, raft::row_major> pq_code_book_view)
66-
: vq_code_book_view_{vq_code_book_view}, pq_code_book_view_{pq_code_book_view}
64+
// PQ codebook view is required; VQ codebook view is optional and defaults to absent.
65+
// When VQ is not provided, vq_code_book() returns std::nullopt.
66+
explicit vpq_codebooks_view(view_type pq_code_book_view,
67+
std::optional<view_type> vq_code_book_view = std::nullopt)
68+
: pq_code_book_view_{pq_code_book_view}, vq_code_book_view_{vq_code_book_view}
6769
{
6870
}
6971

@@ -73,20 +75,18 @@ class vpq_codebooks_view : public vpq_codebooks_iface<MathT> {
7375
vpq_codebooks_view& operator=(vpq_codebooks_view&&) = default;
7476
~vpq_codebooks_view() override = default;
7577

76-
[[nodiscard]] auto vq_code_book() const noexcept
77-
-> raft::device_matrix_view<const math_type, uint32_t, raft::row_major> override
78+
[[nodiscard]] auto vq_code_book() const noexcept -> std::optional<view_type> override
7879
{
7980
return vq_code_book_view_;
8081
}
81-
[[nodiscard]] auto pq_code_book() const noexcept
82-
-> raft::device_matrix_view<const math_type, uint32_t, raft::row_major> override
82+
[[nodiscard]] auto pq_code_book() const noexcept -> view_type override
8383
{
8484
return pq_code_book_view_;
8585
}
8686

8787
private:
88-
raft::device_matrix_view<const math_type, uint32_t, raft::row_major> vq_code_book_view_;
89-
raft::device_matrix_view<const math_type, uint32_t, raft::row_major> pq_code_book_view_;
88+
view_type pq_code_book_view_;
89+
std::optional<view_type> vq_code_book_view_;
9090
};
9191

9292
} // namespace cuvs::preprocessing::quantize::pq

0 commit comments

Comments
 (0)