cuDDL
Loading...
Searching...
No Matches
top_k.cuh
Go to the documentation of this file.
1#pragma once
2
3#include <cub/device/device_segmented_sort.cuh>
4#include <cuda/buffer>
5#include <cuda/iterator>
6#include <cuda/std/bit>
7#include <cuda/std/cstdint>
8#include <cuda/stream>
9
10#include <algorithm>
11#include <span>
12#include <vector>
13
14#include <cuddl/error.hpp>
16
17namespace cuddl {
18
19namespace detail {
20
28[[nodiscard]] __host__ __device__ constexpr uint64_t
29top_k_key(pairwise_counts const& counts, uint32_t id) noexcept {
30 auto const identity = cuda::std::bit_cast<uint32_t>(weighted_kmer_identity(counts));
31 return (uint64_t{identity} << 32U) | static_cast<uint32_t>(~id);
32}
33
37 uint64_t* keys;
38 uint64_t* counts;
39 uint32_t k;
41};
42
45 uint32_t k;
46
47 __host__ __device__ int64_t operator()(int64_t i) const noexcept {
48 return i * k;
49 }
50};
51
53constexpr uint32_t top_k_warps = block_size / 32U;
54
56[[nodiscard]] constexpr size_t top_k_heap_words(uint32_t k) noexcept {
57 return size_t{k} * 2U;
58}
59
65__device__ inline void
66top_k_replace_root(uint64_t* heap, uint32_t k, uint64_t key, uint64_t value) {
67 auto* const values = heap + k;
68 uint32_t at = 0U;
69 for (auto child = 1U; child < k; child = 2U * at + 1U) {
70 auto child_key = heap[child];
71 if (child + 1U < k && heap[child + 1U] < child_key) child_key = heap[++child];
72 if (child_key >= key) break;
73 heap[at] = child_key;
74 values[at] = values[child];
75 at = child;
76 }
77 heap[at] = key;
78 values[at] = value;
79}
80
83template <bool Thresholded, bool UpperTriangle>
84__device__ inline uint32_t
85top_k_word_bits(batch_result_tile const& tile, uint32_t query, uint32_t word, uint32_t words) {
86 uint32_t bits = 0U;
87 if constexpr (Thresholded) {
88 bits = __ldg(tile.pass_bits + static_cast<size_t>(query) * words + word);
89 } else {
90 auto const left = tile.reference_count - word * 32U;
91 bits = left >= 32U ? ~0U : (1U << left) - 1U;
92 }
93 if constexpr (UpperTriangle) {
94 auto const query_id = tile.first_query_id + query;
95 if (word * 32U <= query_id) {
96 auto const skipped = query_id - word * 32U + 1U;
97 bits &= skipped >= 32U ? 0U : ~0U << skipped;
98 }
99 }
100 return bits;
101}
102
104template <bool UpperTriangle>
105__device__ inline packed_pairwise_counts
106top_k_stored(batch_result_tile const& tile, uint32_t query, uint32_t reference) {
107 auto const slot = batch_result_slot<UpperTriangle>(
108 query, reference, tile.first_query_id, tile.reference_count
109 );
110 return packed_pairwise_counts{__ldg(&tile.results[slot].bits)};
111}
112
114__device__ inline uint32_t top_k_nth_bit(uint32_t mask, uint32_t n) {
115 uint32_t position = 0U;
116 asm("fns.b32 %0, %1, 0, %2;" : "=r"(position) : "r"(mask), "r"(static_cast<int>(n + 1U)));
117 return position;
118}
119
129template <typename Bits, typename Entry>
130__device__ inline void
131top_k_warp_merge(uint64_t* heap, uint32_t k, uint32_t words, Bits bits_of, Entry entry_of) {
132 auto const lane = static_cast<uint32_t>(threadIdx.x) % 32U;
133 for (uint32_t base = 0U; base < words; base += 32U) {
134 auto const bits = base + lane < words ? bits_of(base + lane) : 0U;
135 // Inclusive prefix of the lanes' set bits, so candidate j is in the first lane whose
136 // prefix exceeds j.
137 auto const count = static_cast<uint32_t>(cuda::std::popcount(bits));
138 auto prefix = count;
139 _Pragma("unroll")
140 for (uint32_t offset = 1U; offset < 32U; offset *= 2U) {
141 auto const below = __shfl_up_sync(~0U, prefix, offset);
142 if (lane >= offset) prefix += below;
143 }
144 auto const total = __shfl_sync(~0U, prefix, 31);
145 for (uint32_t first = 0U; first < total; first += 32U) {
146 auto const candidate = first + lane;
147 // Every lane takes part in the search's shuffles, with or without a candidate.
148 uint32_t source = 0U;
149 _Pragma("unroll")
150 for (uint32_t step = 16U; step != 0U; step /= 2U) {
151 if (__shfl_sync(~0U, prefix, source + step - 1U) <= candidate) source += step;
152 }
153 auto const source_bits = __shfl_sync(~0U, bits, source);
154 auto const before = __shfl_sync(~0U, prefix - count, source);
155 uint64_t key = 0U, value = 0U;
156 if (candidate < total) {
157 entry_of(
158 (base + source) * 32U + top_k_nth_bit(source_bits, candidate - before),
159 key,
160 value
161 );
162 }
163 for (auto pending = __ballot_sync(~0U, key > heap[0]); pending != 0U;
164 pending &= pending - 1U) {
165 if (lane == static_cast<uint32_t>(__ffs(pending) - 1) && key > heap[0]) {
166 top_k_replace_root(heap, k, key, value);
167 }
168 __syncwarp();
169 }
170 }
171 }
172}
173
176template <bool ToShared>
177__device__ inline void top_k_warp_copy(top_k_slots const& slots, size_t row, uint64_t* heap) {
178 auto const k = slots.k;
179 for (auto i = static_cast<uint32_t>(threadIdx.x) % 32U; i < k; i += 32U) {
180 if constexpr (ToShared) {
181 heap[i] = slots.keys[row * k + i];
182 heap[k + i] = slots.counts[row * k + i];
183 } else {
184 slots.keys[row * k + i] = heap[i];
185 slots.counts[row * k + i] = heap[k + i];
186 }
187 }
188 __syncwarp();
189}
190
192template <bool Thresholded, bool UpperTriangle>
193__global__ __launch_bounds__(
195) void top_k_rows_kernel(batch_result_tile tile, top_k_slots slots) {
196 extern __shared__ uint64_t heaps[];
197 auto const warp = static_cast<uint32_t>(threadIdx.x) / 32U;
198 auto const query = static_cast<uint32_t>(blockIdx.x) * (blockDim.x / 32U) + warp;
199 if (query >= tile.query_count) return;
200 auto* const heap = heaps + warp * top_k_heap_words(slots.k);
201 auto const query_id = tile.first_query_id + query;
202 auto const row = static_cast<size_t>(query_id - slots.first_genome_id);
204 auto const words = tile.words_per_query();
205 // All-to-all rows start after the diagonal's word.
206 auto const skipped = UpperTriangle ? (query_id + 1U) / 32U : 0U;
208 heap,
209 slots.k,
210 words - cuda::std::min(skipped, words),
211 [&](uint32_t word) {
212 return top_k_word_bits<Thresholded, UpperTriangle>(tile, query, skipped + word, words);
213 },
214 [&](uint32_t candidate, uint64_t& key, uint64_t& value) {
215 auto const reference = skipped * 32U + candidate;
216 auto const stored = top_k_stored<UpperTriangle>(tile, query, reference);
217 key = top_k_key(stored.unpack(tile.bucket_count), reference);
218 value = stored.bits;
219 }
220 );
222}
223
231template <bool Thresholded>
232__global__ __launch_bounds__(
234) void top_k_transpose_kernel(batch_result_tile tile, uint32_t* columns, uint32_t first_word) {
235 auto const lane = static_cast<uint32_t>(threadIdx.x) % 32U;
236 auto const block = static_cast<uint64_t>(blockIdx.x) * (blockDim.x / 32U) + threadIdx.x / 32U;
237 auto const words = tile.words_per_query();
239 auto const row_words = (tile.query_count + 31U) / 32U;
240 if (block >= static_cast<uint64_t>(row_words) * word_count) return;
241 auto const word = first_word + static_cast<uint32_t>(block % word_count);
242 auto const row_word = static_cast<uint32_t>(block / word_count);
243 auto const query = row_word * 32U + lane;
244 auto const bits = query < tile.query_count
245 ? top_k_word_bits<Thresholded, true>(tile, query, word, words)
246 : 0U;
247 uint32_t column = 0U;
248 _Pragma("unroll")
249 for (uint32_t bit = 0U; bit < 32U; ++bit) {
250 auto const passing = __ballot_sync(~0U, ((bits >> bit) & 1U) != 0U);
251 if (lane == bit) column = passing;
252 }
253 auto const genome = word * 32U + lane;
254 if (genome < tile.reference_count) {
255 columns[static_cast<size_t>(row_word) * tile.reference_count + genome] = column;
256 }
257}
258
264__global__ __launch_bounds__(block_size) void top_k_columns_kernel(
265 batch_result_tile tile,
266 top_k_slots slots,
267 uint32_t const* columns,
268 uint32_t first_genome
269) {
270 extern __shared__ uint64_t heaps[];
271 auto const warp = static_cast<uint32_t>(threadIdx.x) / 32U;
272 auto const genome =
273 first_genome + static_cast<uint32_t>(blockIdx.x) * (blockDim.x / 32U) + warp;
274 if (genome >= tile.reference_count) return;
275 auto* const heap = heaps + warp * top_k_heap_words(slots.k);
277 // Rows before the genome, whose pairs with it the tile stores in their rows.
278 auto const rows = cuda::std::min(tile.query_count, genome - tile.first_query_id);
280 heap,
281 slots.k,
282 (rows + 31U) / 32U,
283 [&](uint32_t row_word) {
284 return __ldg(columns + static_cast<size_t>(row_word) * tile.reference_count + genome);
285 },
286 [&](uint32_t query, uint64_t& key, uint64_t& value) {
287 auto const counts =
288 top_k_stored<true>(tile, query, genome).unpack(tile.bucket_count).mirrored();
289 key = top_k_key(counts, tile.first_query_id + query);
290 value = packed_pairwise_counts::pack(counts.lower, counts.equal, counts.higher).bits;
291 }
292 );
294}
295
296} // namespace detail
297
318 public:
328 [[nodiscard]] static Result<top_k_search> make(
329 uint32_t genome_count,
330 uint32_t reference_count,
331 uint32_t bucket_count,
332 uint32_t k,
333 cuda::stream_ref stream,
334 uint32_t first_genome_id = 0U
335 ) {
336 if (k == 0U) return Err(Error::invalid_argument("top-k search needs k of at least 1"));
337 detail::device_scope const scope{stream};
338 int shared_limit = 0;
339 CUDDL_CUDA_TRY(cudaDeviceGetAttribute(
340 &shared_limit, cudaDevAttrMaxSharedMemoryPerBlockOptin, stream.device().get()
341 ));
342 auto const heap_bytes = detail::top_k_heap_words(k) * sizeof(uint64_t);
343 auto const warps = static_cast<uint32_t>(
344 std::min<size_t>(detail::top_k_warps, static_cast<size_t>(shared_limit) / heap_bytes)
345 );
346 if (warps == 0U) {
347 return Err(Error::invalid_argument("top-k heap exceeds this GPU's shared memory"));
348 }
349 auto const bytes = warps * heap_bytes;
350 CUDDL_TRY(allow_shared(detail::top_k_rows_kernel<false, false>, bytes));
351 CUDDL_TRY(allow_shared(detail::top_k_rows_kernel<true, false>, bytes));
352 CUDDL_TRY(allow_shared(detail::top_k_rows_kernel<false, true>, bytes));
353 CUDDL_TRY(allow_shared(detail::top_k_rows_kernel<true, true>, bytes));
354 CUDDL_TRY(allow_shared(detail::top_k_columns_kernel, bytes));
355 auto const slots = static_cast<size_t>(genome_count) * k;
357 genome_count,
358 reference_count,
359 bucket_count,
360 k,
361 first_genome_id,
362 warps,
363 cuda::make_device_buffer<uint64_t>(stream, stream.device(), slots, uint64_t{0U}),
364 cuda::make_device_buffer<uint64_t>(stream, stream.device(), slots, cuda::no_init),
365 cuda::make_device_buffer<uint32_t>(stream, stream.device(), 0U, cuda::no_init)
366 ));
367 }
368
371 [[nodiscard]] Result<void> add(batch_result_tile const& tile, cuda::stream_ref stream) {
372 detail::device_scope const scope{stream};
373 if (tile.reference_count != reference_count_) {
374 return Err(Error::invalid_argument("tile belongs to a database of another size"));
375 }
376 if (tile.query_count > genome_count_ || tile.first_query_id < first_genome_id_ ||
377 tile.first_query_id - first_genome_id_ > genome_count_ - tile.query_count) {
378 return Err(Error::invalid_argument("tile rows lie outside the collected genomes"));
379 }
380 if (tile.word_offsets != nullptr) {
381 return Err(Error::invalid_argument("top-k search takes device tiles, not host copies"));
382 }
383 if (tile.upper_triangle && (first_genome_id_ != 0U || genome_count_ != reference_count_)) {
384 return Err(
385 Error::invalid_argument("an all-to-all tile needs a collector of every row")
386 );
387 }
388 if (tile.query_count == 0U || reference_count_ == 0U) return Ok();
389 detail::top_k_slots const slots{keys_.data(), counts_.data(), k_, first_genome_id_};
390 auto const thresholded = tile.pass_bits != nullptr;
391 auto const heap_bytes = detail::top_k_heap_words(k_) * sizeof(uint64_t);
392 auto const rows = tile.upper_triangle
393 ? (thresholded ? detail::top_k_rows_kernel<true, true>
394 : detail::top_k_rows_kernel<false, true>)
395 : (thresholded ? detail::top_k_rows_kernel<true, false>
396 : detail::top_k_rows_kernel<false, false>);
397 auto const shared = warps_ * heap_bytes;
398 rows<<<(tile.query_count + warps_ - 1U) / warps_, warps_ * 32U, shared, stream.get()>>>(
399 tile, slots
400 );
401 CUDDL_CUDA_TRY(cudaGetLastError());
402 if (!tile.upper_triangle) return Ok();
403 // Genomes after the tile's first row take its rows before them as candidates.
404 auto const first_genome = tile.first_query_id + 1U;
405 if (first_genome >= reference_count_) return Ok();
406 auto const first_word = first_genome / 32U;
407 auto const row_words = (tile.query_count + 31U) / 32U;
408 auto const needed = static_cast<size_t>(row_words) * reference_count_;
409 if (columns_.size() < needed) {
410 columns_ = CUDDL_CUDA_TRY(
411 cuda::make_device_buffer<uint32_t>(stream, stream.device(), needed, cuda::no_init)
412 );
413 }
414 // One warp per 32-by-32 block of the pass bits from the first genome's word on.
415 auto const bit_blocks =
416 static_cast<uint64_t>(row_words) * (tile.words_per_query() - first_word);
417 auto const warps_per_block = detail::block_size / 32U;
418 (thresholded ? detail::top_k_transpose_kernel<true>
419 : detail::top_k_transpose_kernel<false>)<<<
420 static_cast<uint32_t>((bit_blocks + warps_per_block - 1U) / warps_per_block),
422 0,
423 stream.get()>>>(tile, columns_.data(), first_word);
424 CUDDL_CUDA_TRY(cudaGetLastError());
425 auto const genomes = reference_count_ - first_genome;
426 detail::top_k_columns_kernel<<<
427 (genomes + warps_ - 1U) / warps_,
428 warps_ * 32U,
429 shared,
430 stream.get()>>>(tile, slots, columns_.data(), first_genome);
431 CUDDL_CUDA_TRY(cudaGetLastError());
432 return Ok();
433 }
434
436 [[nodiscard]] Result<std::vector<batch_search_result>> download(cuda::stream_ref stream) const {
437 detail::device_scope const scope{stream};
438 auto const slots = keys_.size();
439 std::vector<uint64_t> keys(slots), counts(slots);
440 if (slots != 0U) {
441 // Rows are heaps on the device; sorting them there leaves the host a linear pass.
442 auto sorted_keys = CUDDL_CUDA_TRY(
443 cuda::make_device_buffer<uint64_t>(stream, stream.device(), slots, cuda::no_init)
444 );
445 auto sorted_counts = CUDDL_CUDA_TRY(
446 cuda::make_device_buffer<uint64_t>(stream, stream.device(), slots, cuda::no_init)
447 );
448 auto const offsets = cuda::make_transform_iterator(
449 cuda::counting_iterator<int64_t>{0}, detail::top_k_row_offset{k_}
450 );
452 cub::DeviceSegmentedSort::SortPairsDescending(
453 keys_.data(),
454 sorted_keys.data(),
455 counts_.data(),
456 sorted_counts.data(),
457 static_cast<int64_t>(slots),
458 static_cast<int64_t>(genome_count_),
459 offsets,
460 offsets + 1,
461 stream
462 )
463 );
464 CUDDL_CUDA_TRY(cuda::copy_bytes(stream, sorted_keys, cuda::std::span{keys}));
465 CUDDL_CUDA_TRY(cuda::copy_bytes(stream, sorted_counts, cuda::std::span{counts}));
466 }
467 CUDDL_CUDA_TRY(stream.sync());
468 std::vector<batch_search_result> rows;
469 rows.reserve(slots);
470 for (size_t slot = 0U; slot < slots; ++slot) {
471 if (keys[slot] == 0U) continue;
472 rows.push_back({
473 .query_id = first_genome_id_ + static_cast<uint32_t>(slot / k_),
474 .reference_id = ~static_cast<uint32_t>(keys[slot]),
475 .counts = packed_pairwise_counts{counts[slot]}.unpack(bucket_count_),
476 });
477 }
478 return rows;
479 }
480
481 [[nodiscard]] uint32_t k() const noexcept {
482 return k_;
483 }
484
485 private:
487 uint32_t genome_count,
488 uint32_t reference_count,
489 uint32_t bucket_count,
490 uint32_t k,
491 uint32_t first_genome_id,
492 uint32_t warps,
493 cuda::device_buffer<uint64_t> keys,
494 cuda::device_buffer<uint64_t> counts,
495 cuda::device_buffer<uint32_t> columns
496 )
497 : genome_count_(genome_count),
498 reference_count_(reference_count),
499 bucket_count_(bucket_count),
500 k_(k),
501 first_genome_id_(first_genome_id),
502 warps_(warps),
503 keys_(std::move(keys)),
504 counts_(std::move(counts)),
505 columns_(std::move(columns)) {}
506
509 template <typename Kernel>
510 [[nodiscard]] static Result<void> allow_shared(Kernel kernel, size_t bytes) {
511 return cuda_try(cudaFuncSetAttribute(
512 kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, static_cast<int>(bytes)
513 ));
514 }
515
516 uint32_t genome_count_;
517 uint32_t reference_count_;
518 uint32_t bucket_count_;
519 uint32_t k_;
520 uint32_t first_genome_id_;
522 uint32_t warps_;
523 cuda::device_buffer<uint64_t> keys_, counts_;
525 cuda::device_buffer<uint32_t> columns_;
526};
527
532[[nodiscard]] inline std::vector<batch_search_result>
533select_top_k(std::vector<batch_search_result> rows, uint32_t k) {
534 std::ranges::sort(rows, [](batch_search_result const& a, batch_search_result const& b) {
535 if (a.query_id != b.query_id) return a.query_id < b.query_id;
538 });
539 std::vector<batch_search_result> kept;
540 for (size_t first = 0U; first < rows.size();) {
541 auto last = first;
542 while (last < rows.size() && rows[last].query_id == rows[first].query_id) ++last;
543 kept.insert(
544 kept.end(),
545 rows.begin() + static_cast<std::ptrdiff_t>(first),
546 rows.begin() + static_cast<std::ptrdiff_t>(std::min<size_t>(last, first + k))
547 );
548 first = last;
549 }
550 return kept;
551}
552
553} // namespace cuddl
Keeps each genome's k most similar genomes from search tiles on the device.
Definition top_k.cuh:317
Result< void > add(batch_result_tile const &tile, cuda::stream_ref stream)
Merges tile's passing pairs into the running neighbours, on stream.
Definition top_k.cuh:371
uint32_t k() const noexcept
Definition top_k.cuh:481
Result< std::vector< batch_search_result > > download(cuda::stream_ref stream) const
Each genome's neighbours, best first, genome by genome; waits for stream.
Definition top_k.cuh:436
static Result< top_k_search > make(uint32_t genome_count, uint32_t reference_count, uint32_t bucket_count, uint32_t k, cuda::stream_ref stream, uint32_t first_genome_id=0U)
Definition top_k.cuh:328
#define CUDDL_CUDA_TRY(...)
Propagates a CUDA error wrapped in cuddl::Result<void>.
Definition error.hpp:248
#define CUDDL_TRY(expr)
Propagates a cuddl::Result failure from the enclosing function (GNU statement expression).
Definition error.hpp:225
constexpr uint32_t row_words
Definition kernels.cuh:1546
auto const row_word
Definition top_k.cuh:242
auto const * query
Definition kernels.cuh:1030
auto *const heap
Definition top_k.cuh:200
_Pragma("unroll") for(uint32_t bit=0U
auto const block
Definition top_k.cuh:236
auto const skipped
Definition top_k.cuh:206
constexpr uint32_t top_k_warps
Warps per merging block, each holding one heap; fewer when the heaps exceed shared memory.
Definition top_k.cuh:53
auto const lane
Definition kernels.cuh:966
auto const count
Definition kernels.cuh:1031
auto const warp
Definition kernels.cuh:967
top_k_warp_copy< false >(slots, row, heap)
constexpr size_t top_k_heap_words(uint32_t k) noexcept
Words of shared memory one heap takes: k keys, then their k packed counts.
Definition top_k.cuh:56
__host__ __device__ constexpr uint64_t top_k_key(pairwise_counts const &counts, uint32_t id) noexcept
Ranking key of a neighbour: its weighted k-mer identity above its complemented ID.
Definition top_k.cuh:29
constexpr uint32_t block_size
Threads per CTA for the single-pair and cardinality reduction kernels.
Definition kernels.cuh:28
__global__ uint32_t * columns
Definition top_k.cuh:234
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t tile
Definition kernels.cuh:922
__global__ top_k_slots slots
Definition top_k.cuh:195
__device__ void top_k_warp_copy(top_k_slots const &slots, size_t row, uint64_t *heap)
Copies the k entries of global heap row into, or for ToShared false out of, the shared heap heap with...
Definition top_k.cuh:177
__device__ packed_pairwise_counts top_k_stored(batch_result_tile const &tile, uint32_t query, uint32_t reference)
The stored result of tile row query and reference, which must pass.
Definition top_k.cuh:106
uint32_t column
Definition top_k.cuh:247
auto const total
Definition kernels.cuh:443
__global__ uint32_t uint32_t first_word
Definition top_k.cuh:234
__device__ void top_k_warp_merge(uint64_t *heap, uint32_t k, uint32_t words, Bits bits_of, Entry entry_of)
One warp merges the candidates of words bitmap words into the heap of k entries at heap,...
Definition top_k.cuh:131
__device__ uint32_t top_k_nth_bit(uint32_t mask, uint32_t n)
Position of set bit n, counting from zero, of mask, which has more than n set bits.
Definition top_k.cuh:114
__device__ void top_k_replace_root(uint64_t *heap, uint32_t k, uint64_t key, uint64_t value)
Replaces the root of a min-heap of k keys at heap, whose values follow the keys, with key and value,...
Definition top_k.cuh:66
auto const words
Definition top_k.cuh:204
::cuda::__ensure_current_context device_scope
Makes stream's device current for the enclosing scope and restores the previous context on exit.
__global__ __launch_bounds__(shared_construction_block_size) void add_shared_kernel(uint64_t const *input
Constructs a sketch through a CTA-local shared-memory register array.
auto const word_count
Definition top_k.cuh:238
for(auto i=static_cast< size_t >(blockIdx.x) *block_size+threadIdx.x;i< total;i+=static_cast< size_t >(gridDim.x) *block_size)
Definition kernels.cuh:444
__device__ uint32_t top_k_word_bits(batch_result_tile const &tile, uint32_t query, uint32_t word, uint32_t words)
The pass bits of word word of tile row query: the stored bits, or for exhaustive tiles every referenc...
Definition top_k.cuh:85
auto *const bits
Definition kernels.cuh:1184
top_k_warp_copy< true >(slots, row, heap)
auto const word
Definition top_k.cuh:241
__global__ size_t uint16_t * rows
Definition kernels.cuh:1499
auto const row
Definition top_k.cuh:202
auto const query_id
Definition top_k.cuh:201
Batch operations over contiguous device rows.
Definition a48.hpp:14
@ counts
One 24-byte batch_search_result per pair: query ID, reference ID and the lower, equal,...
Result< void > Ok() noexcept
Success return for Result<void>; same as return {}.
Definition error.hpp:181
cuda::std::unexpected< Error > Err(Error error)
Failure return; converts to any Result<T> via cuda::std::unexpected.
Definition error.hpp:176
__host__ __device__ constexpr float weighted_kmer_identity(pairwise_counts const &counts) noexcept
Weighted k-mer identity of a pair, equal / max(equal + min(lower, higher), 6), as sketch::wkid comput...
Result< void > cuda_try(cudaError_t error, std::source_location location=std::source_location::current())
Checks a CUDA runtime call and returns an error on failure.
Definition error.hpp:187
std::vector< batch_search_result > select_top_k(std::vector< batch_search_result > rows, uint32_t k)
The k best rows of each query among rows, as top_k_search ranks them, best first and query by query.
Definition top_k.cuh:533
static Error invalid_argument(std::string message)
Definition error.hpp:99
Fallible API result: cuda::std::expected<T, Error> with cuDDL factories.
Definition error.hpp:118
Non-owning view of one batch-search tile's results.
Stable query/reference IDs and their exact query-relative bucket comparison counts.
Start of genome row i of a collector's heaps, rows of k slots.
Definition top_k.cuh:44
__host__ __device__ int64_t operator()(int64_t i) const noexcept
Definition top_k.cuh:47
Each genome's k best neighbours so far: keys and packed counts in parallel rows of k,...
Definition top_k.cuh:36
One pair's exact bucket comparison counts in 64 bits, the storage and transfer form of batch-search r...
__host__ __device__ constexpr pairwise_counts unpack(uint32_t bucket_count) const noexcept
Raw per-bucket classification counts for one sketch pair comparison.