Conversation
3c02fb5 to
3381152
Compare
3381152 to
0e5727a
Compare
|
Depends on VectorDB-NTU/RaBitQ-Library#36 |
There was a problem hiding this comment.
this class can be moved to quantizer folder with rabitq_reformer
There was a problem hiding this comment.
rabitq_converter/reformer is specialized for rabitq. It's better to put them inside hnsw_rabitq for now.
|
|
||
| void HnswRabitqQueryAlgorithm::expand_neighbors_by_group( | ||
| TopkHeap &topk, HnswRabitqContext *ctx) const { | ||
| // if (!ctx->group_by().is_valid()) { |
There was a problem hiding this comment.
to return with some message when group's setup?
There was a problem hiding this comment.
implemented group by search, will test it after framework support group by .
| continue; | ||
| } | ||
| } else { | ||
| // Candidate cand{ResultRecord(candest.est_dist, candest.low_dist), |
There was a problem hiding this comment.
no need to check ex bits here? can it be checked earlier?
There was a problem hiding this comment.
It's necessary to check ex_bits here.
- first get_bin_est use single bit to estimate score
- ex_bits > 0
- yes. check if need to do full estimate with extra bits
- no. Nothing to be done since we already used all the information.
|
|
||
| // TODO: check loop type | ||
|
|
||
| // if ((!topk.full()) || cur_dist < topk[0].second) { |
There was a problem hiding this comment.
lagacy codes here and elsewhere can be cleaned up.
| query_wrapper.set_g_add(q_to_centroids[cluster_id]); | ||
| float est_dist = rabitqlib::split_distance_boosting( | ||
| ex_data, ip_func_, query_wrapper, padded_dim_, ex_bits_, res.ip_x0_qr); | ||
| float low_dist = est_dist - (res.est_dist - res.low_dist) / (1 << ex_bits_); |
There was a problem hiding this comment.
can predefine param to convert to multiply manipulation:
double inv_divisor = 1.0 / (1 << ex_bits_);
auto result = (res.est_dist - res.low_dist) * inv_divisor;
There was a problem hiding this comment.
get_ex_est is not used, deleted.
|
@greptile |
|
@Greptile |
resolve #42
Greptile Summary
This PR adds a new HNSW-RaBitQ index type to zvec, integrating scalar quantization (RaBitQ) into the HNSW graph structure to reduce memory usage and accelerate approximate nearest-neighbour search. The change is large (~14,500 lines across 96 files) and touches the full index lifecycle: builder, searcher, streamer, Python bindings, DB integration, and tests.
Key observations:
RabitqConverter→RabitqReformer) cleanly separates training/conversion from the HNSW graph code.search_bf_by_p_keys_implstub issues have been addressed.cluster->mount()return value is silently discarded inrabitq_converter.cc:173— if mounting the sampler fails, training continues and produces misleading downstream errors rather than an actionable failure.rand()is used for MemoryDumper file-ID generation in bothhnsw_rabitq_builder.cc:293andrabitq_converter.cc:261.rand()relies on global state and is not thread-safe; while training is currently single-threaded, this is a latent hazard.std::make_sharedinhnsw_rabitq_builder.cc:174–178:std::make_sharedthrows on failure and never returns null; the guard is misleading.RABITQ_CONVERER_SEG_IDconstant name typo inrabitq_utils.h:24(missingTinCONVERTER). The value string is correct so there is no runtime impact, but the misspelling propagates across many files.// TODO: release memory of memory_storagecomments remain unresolved inhnsw_rabitq_builder.cc:329andrabitq_converter.cc:298; these should be tracked or resolved before a stable release.Confidence Score: 3/5
cluster->mount()return value can mask training failures, and the two unresolved memory-release TODOs represent acknowledged but unaddressed resource management concerns. These lower confidence from a clean merge.src/core/algorithm/hnsw_rabitq/rabitq_converter.cc(ignored mount() return value) andsrc/core/algorithm/hnsw_rabitq/hnsw_rabitq_builder.cc(TODO memory release, rand() usage).Important Files Changed
std::make_sharedand uses the non-thread-saferand()for MemoryDumper file ID generation. Also carries an unresolved// TODO: release memory of memory_storageat line 329.cluster->mount(sampler)is silently ignored (line 173), which can cause misleading downstream errors. Also usesrand()(not thread-safe) for MemoryDumper file IDs and carries a// TODO: release memory of memory_storagecomment.expand_neighbors_by_groupfunction's inner-loopbreakon group_num does not terminate the outerwhileloop (outer loop continues consuming candidates). This could cause extra work but was flagged in previous review threads.std::vector. No new critical issues.RabitqConverterHeaderstruct. The constant nameRABITQ_CONVERER_SEG_IDis misspelled (missing 'T') but the string value is correct, so no runtime impact. Thestatic_assertensuring 32-byte alignment is a good practice.Sequence Diagram
sequenceDiagram participant Caller participant Builder as HnswRabitqBuilder participant Converter as RabitqConverter participant Reformer as RabitqReformer participant Entity as HnswRabitqEntity participant Alg as HnswRabitqAlgorithm Caller->>Builder: init(meta, params) Builder->>Converter: init(meta, params) Builder->>Entity: init() Builder->>Alg: init() Caller->>Builder: train(holder) Builder->>Converter: train(holder) [KMeans clustering] Converter-->>Builder: centroids ready Builder->>Converter: dump(MemoryDumper) Builder->>Reformer: init(params) Builder->>Reformer: load(MemoryReadStorage) Reformer-->>Builder: reformer ready Caller->>Builder: build(holder) loop for each vector in holder Builder->>Reformer: convert(raw_vec) → quantized_vec Builder->>Entity: add_vector(level, key, quantized_vec) end loop parallel threads Builder->>Alg: add_node(id, level, ctx) Alg->>Entity: get/update neighbors end Caller->>Builder: dump(dumper) Builder->>Converter: dump(dumper) [centroids] Builder->>Entity: dump(dumper) [graph data] note over Caller,Alg: Search path (HnswRabitqSearcher) Caller->>Reformer: transform_to_entity(query) → QueryEntity Caller->>Alg: search(QueryEntity, ctx) Alg->>Entity: get_neighbors / get_vector Alg-->>Caller: topk resultsLast reviewed commit: ad5a807