cuDDL
Loading...
Searching...
No Matches
sketch.cuh
Go to the documentation of this file.
1#pragma once
2
3#include <cub/version.cuh>
4#include <cuda/buffer>
5#include <cuda/memory_pool>
6#include <cuda/std/cstdint>
7#include <cuda/stream>
8
9#include <algorithm>
10#include <cmath>
11#include <memory>
12#include <optional>
13#include <utility>
14#include <vector>
15
18#include <cuddl/error.hpp>
21
22namespace cuddl {
23
24static_assert(
25 CUB_VERSION == 300600,
26 "cuDDL requires CUB 3.6.0 from CCCL revision 486de1c44daf7d1a343cbbf0f5477e9bea8ad600; "
27 "use the Meson cccl subproject."
28);
29
30namespace detail {
31
34 uint64_t empty;
35 double estimate;
36};
37
48
49} // namespace detail
50
62template <uint32_t K, size_t BucketCount, typename Layout = default_register_layout>
63class sketch {
64 public:
65 using layout_type = Layout;
66 using register_type = uint32_t;
67
71 explicit sketch(
72 cuda::stream_ref stream,
73 std::optional<std::shared_ptr<device_blacklist const>> blacklist = std::nullopt
74 )
75 : blacklist_(std::move(blacklist)),
76 storage_(
77 cuda::make_device_buffer<register_type>(
78 stream,
79 stream.device(),
80 BucketCount,
82 )
83 ),
84 mapped_scratch_(
85 stream,
86 detail::device_pinned_pool(stream.device(), detail::pinned_placement::device_node),
87 1,
88 cuda::no_init
89 ) {
90 if (blacklist_) {
91 if (!*blacklist_) throw std::invalid_argument("blacklist owner must not be null");
92 CUDDL_UNWRAP((*blacklist_)->validate(K, BucketCount, stream));
93 }
94 stream.sync();
95 }
96
97 sketch(sketch const&) = delete;
98 sketch& operator=(sketch const&) = delete;
99 sketch(sketch&&) noexcept = default;
101 sketch& operator=(sketch&&) noexcept = default;
102
107 [[nodiscard]] device_span<register_type> data() noexcept {
108 added_ = std::nullopt;
109 return {storage_.data(), BucketCount};
110 }
111
113 [[nodiscard]] device_span<register_type const> data() const noexcept {
114 return {storage_.data(), BucketCount};
115 }
116
118 [[nodiscard]] static constexpr size_t bucket_count() noexcept {
119 return BucketCount;
120 }
121
123 [[nodiscard]] static constexpr uint32_t kmer_length() noexcept {
124 return K;
125 }
126
134 [[nodiscard]] std::optional<uint64_t> added() const noexcept {
135 return added_;
136 }
137
142 [[nodiscard]] Result<void> clear_async(cuda::stream_ref stream) const noexcept {
143 if (auto const result = view().clear_async(stream); !result) {
144 return result;
145 }
146 added_ = 0;
147 return {};
148 }
149
151 [[nodiscard]] Result<void> clear(cuda::stream_ref stream) const noexcept {
152 if (auto const result = clear_async(stream); !result) {
153 return result;
154 }
155 return cuda_try([&] { stream.sync(); });
156 }
157
167 cuda::stream_ref stream,
168 kmer_blacklist const& source_blacklist
169 ) const noexcept {
170 if (source_blacklist.identity() != blacklist_identity() ||
171 (!source_blacklist.keys().empty() && source_blacklist.kmer_length() != K)) {
172 return Err(Error::invalid_argument("assigned registers use a different blacklist"));
173 }
174 if (registers.size() != BucketCount) {
175 return Err(Error::invalid_argument("sketch assign needs one register per bucket"));
176 }
177 auto* const destination = storage_.data();
178 if (auto const result = cuda_try([&] {
179 cuda::copy_bytes(
180 stream,
181 cuda::std::span{registers.data(), registers.size()},
182 cuda::std::span{destination, BucketCount}
183 );
184 });
185 !result) {
186 return result;
187 }
188 added_ = std::nullopt;
189 return {};
190 }
191
199 [[nodiscard]] Result<void>
200 add_async(device_span<uint64_t const> input, cuda::stream_ref stream) const noexcept {
201 if (auto const result = view().add_async(input, stream); !result) {
202 return result;
203 }
204 if (added_.has_value()) {
205 *added_ += input.size();
206 }
207 return {};
208 }
209
212 [[nodiscard]] Result<void>
213 add(device_span<uint64_t const> input, cuda::stream_ref stream) const noexcept {
214 if (auto const result = add_async(input, stream); !result) {
215 return result;
216 }
217 return cuda_try([&] { stream.sync(); });
218 }
219
230 [[nodiscard]] Result<void>
231 add_sequence_async(device_span<char const> sequence, cuda::stream_ref stream) const noexcept {
232 if (auto const result = view().add_sequence_async(sequence, stream); !result) {
233 return result;
234 }
235 if (added_.has_value() && sequence.size() >= K) {
236 *added_ += sequence.size() - K + 1;
237 }
238 return {};
239 }
240
246 [[nodiscard]] Result<void>
247 add_sequence(device_span<char const> sequence, cuda::stream_ref stream) const noexcept {
248 if (auto const result = add_sequence_async(sequence, stream); !result) {
249 return result;
250 }
251 return cuda_try([&] { stream.sync(); });
252 }
253
255 template <bool IncludeCardinality = false>
257 sketch const& other,
258 pairwise_summary& output,
259 cuda::stream_ref stream
260 ) const noexcept {
261 if (blacklist_identity() != other.blacklist_identity()) {
262 return Err(Error::invalid_argument("sketch blacklist mismatch"));
263 }
264 return view().template summary_async<IncludeCardinality>(other.view(), output, stream);
265 }
266
269 sketch const& other,
270 pairwise_summary& output,
271 cuda::stream_ref stream
272 ) const noexcept {
273 return summary_async(other, output, stream);
274 }
275
277 template <bool IncludeCardinality = false>
278 [[nodiscard]] Result<pairwise_summary>
279 summary(sketch const& other, cuda::stream_ref stream) const {
280 auto* const output = &mapped_scratch_.data()->summary;
281 if (auto const result = summary_async<IncludeCardinality>(other, *output, stream);
282 !result) {
283 return Err(result.error());
284 }
285 if (auto const result = cuda_try([&] { stream.sync(); }); !result) {
286 return Err(result.error());
287 }
288 return mapped_scratch_.data()->summary;
289 }
290
292 [[nodiscard]] Result<pairwise_summary>
293 compare(sketch const& other, cuda::stream_ref stream) const {
294 return summary(other, stream);
295 }
296
301 [[nodiscard]] static std::optional<double> wkid(pairwise_summary const& summary) noexcept {
302 auto const equal = summary.counts.equal;
303 auto const divisor = equal + std::min(summary.counts.lower, summary.counts.higher);
304 if (divisor == 0U) {
305 return std::nullopt;
306 }
307 return static_cast<double>(equal) / static_cast<double>(std::max(divisor, 6U));
308 }
309
311 [[nodiscard]] static std::optional<double> ani(pairwise_summary const& summary) noexcept {
312 auto const identity = wkid(summary);
313 if (!identity) {
314 return std::nullopt;
315 }
316 return std::pow(*identity, 1.0 / static_cast<double>(K));
317 }
318
322 [[nodiscard]] static std::optional<double> containment(
324 ) noexcept {
325 auto const divisor = summary.counts.equal + summary.counts.higher;
326 if (divisor == 0U) {
327 return std::nullopt;
328 }
329 return static_cast<double>(summary.counts.equal) / static_cast<double>(divisor);
330 }
331
336 [[nodiscard]] static std::optional<double> completeness(
338 ) noexcept {
339 auto const divisor = summary.counts.equal + summary.counts.lower;
340 if (divisor == 0U) {
341 return std::nullopt;
342 }
343 auto const value = static_cast<double>(summary.counts.equal + summary.counts.higher) /
344 static_cast<double>(divisor);
345 return std::clamp(value, 0.0, 1.0);
346 }
347
353 uint64_t* empty_out,
354 double* estimate_out,
355 cuda::stream_ref stream
356 ) const noexcept {
357 return view().cardinality_async(empty_out, estimate_out, stream);
358 }
359
361 [[nodiscard]] Result<double> cardinality(cuda::stream_ref stream) const {
362 auto* const output = &mapped_scratch_.data()->cardinality;
363 if (auto const result = view().cardinality_async(&output->empty, &output->estimate, stream);
364 !result) {
365 return Err(result.error());
366 }
367 if (auto const result = cuda_try([&] { stream.sync(); }); !result) {
368 return Err(result.error());
369 }
370 return capped(mapped_scratch_.data()->cardinality.estimate);
371 }
372
378 cuda::stream_ref stream
379 ) const {
380 auto* const output = &mapped_scratch_.data()->hybrid;
381 if (auto const result = view().hybrid_cardinality_async(output, stream); !result) {
382 return Err(result.error());
383 }
384 if (auto const result = cuda_try([&] { stream.sync(); }); !result) {
385 return Err(result.error());
386 }
387 auto estimates = mapped_scratch_.data()->hybrid;
388 estimates.bbtools = capped(estimates.bbtools);
389 estimates.paper = capped(estimates.paper);
390 return estimates;
391 }
392
393 private:
395
396 [[nodiscard]] view_type view() const noexcept {
397 return view_type(
398 {storage_.data(), BucketCount}, blacklist_ ? (*blacklist_)->view() : cuda::std::nullopt
399 );
400 }
401
403 [[nodiscard]] double capped(double estimate) const noexcept {
404 return added_.has_value() ? std::min(estimate, static_cast<double>(*added_)) : estimate;
405 }
406
407 [[nodiscard]] uint64_t blacklist_identity() const noexcept {
408 return blacklist_ ? (*blacklist_)->source().identity() : 0;
409 }
410
411 std::optional<std::shared_ptr<device_blacklist const>> blacklist_;
412 mutable cuda::device_buffer<register_type> storage_;
413 mutable cuda::
414 buffer<detail::sketch_scratch, cuda::mr::host_accessible, cuda::mr::device_accessible>
415 mapped_scratch_;
418 mutable std::optional<uint64_t> added_ = 0;
419};
420
421} // namespace cuddl
Non-owning implementation view of a DDL sketch.
Result< void > cardinality_async(uint64_t *empty_out, double *estimate_out, cuda::stream_ref stream) const noexcept
Computes this sketch's cardinality reduction on the GPU.
Immutable canonical k-mers excluded from construction, independent of sketch size.
Definition blacklist.cuh:77
Move-only owning DDL sketch backed by one contiguous device allocation.
Definition sketch.cuh:63
Result< void > add_async(device_span< uint64_t const > input, cuda::stream_ref stream) const noexcept
Adds packed device k-mers to the existing sketch without clearing it.
Definition sketch.cuh:200
sketch(sketch &&) noexcept=default
Result< void > assign_async(device_span< register_type const > registers, cuda::stream_ref stream, kmer_blacklist const &source_blacklist) const noexcept
Overwrites the registers from stored words.
Definition sketch.cuh:165
Result< pairwise_summary > summary(sketch const &other, cuda::stream_ref stream) const
Host-side fused pairwise summary (uses mapped scratch, synchronises).
Definition sketch.cuh:279
static std::optional< double > containment(pairwise_summary const &summary) noexcept
Fraction of the left sketch's content shared with the right sketch.
Definition sketch.cuh:322
static constexpr uint32_t kmer_length() noexcept
Compile-time k-mer length.
Definition sketch.cuh:123
Layout layout_type
Definition sketch.cuh:65
static std::optional< double > completeness(pairwise_summary const &summary) noexcept
Relative cardinality of the left sketch to the right sketch, clamped to [0, 1].
Definition sketch.cuh:336
Result< void > add(device_span< uint64_t const > input, cuda::stream_ref stream) const noexcept
Adds packed device k-mers without clearing, then synchronizes the stream.
Definition sketch.cuh:213
static std::optional< double > wkid(pairwise_summary const &summary) noexcept
Weighted k-mer identity from a raw pair summary.
Definition sketch.cuh:301
Result< void > summary_async(sketch const &other, pairwise_summary &output, cuda::stream_ref stream) const noexcept
Computes a fused pairwise summary into caller-owned device storage.
Definition sketch.cuh:256
std::optional< uint64_t > added() const noexcept
k-mers offered through this sketch's own add paths since the last clear.
Definition sketch.cuh:134
Result< hybrid_cardinality_estimates > hybrid_cardinality(cuda::stream_ref stream) const
Experimental BBTools and paper-style HybridDDL estimates for comparison.
Definition sketch.cuh:377
sketch(sketch const &)=delete
Result< void > cardinality_async(uint64_t *empty_out, double *estimate_out, cuda::stream_ref stream) const noexcept
Computes this sketch's hybridDDL cardinality reduction on the GPU.
Definition sketch.cuh:352
static constexpr size_t bucket_count() noexcept
Number of registers in the sketch.
Definition sketch.cuh:118
Result< pairwise_summary > compare(sketch const &other, cuda::stream_ref stream) const
Raw pairwise counts against other (host result).
Definition sketch.cuh:293
device_span< register_type > data() noexcept
Device registers.
Definition sketch.cuh:107
Result< void > clear_async(cuda::stream_ref stream) const noexcept
Resets every register to zero without synchronising.
Definition sketch.cuh:142
sketch & operator=(sketch const &)=delete
Result< void > clear(cuda::stream_ref stream) const noexcept
Resets every register to zero, then synchronises.
Definition sketch.cuh:151
Result< void > add_sequence_async(device_span< char const > sequence, cuda::stream_ref stream) const noexcept
Adds device-resident raw ASCII bases without clearing.
Definition sketch.cuh:231
Result< void > compare_async(sketch const &other, pairwise_summary &output, cuda::stream_ref stream) const noexcept
Computes raw pairwise counts into caller-owned device storage.
Definition sketch.cuh:268
device_span< register_type const > data() const noexcept
Device registers.
Definition sketch.cuh:113
uint32_t register_type
Definition sketch.cuh:66
Result< void > add_sequence(device_span< char const > sequence, cuda::stream_ref stream) const noexcept
Adds device-resident raw ASCII bases without clearing, then synchronizes.
Definition sketch.cuh:247
sketch(cuda::stream_ref stream, std::optional< std::shared_ptr< device_blacklist const > > blacklist=std::nullopt)
Allocates and clears the sketch.
Definition sketch.cuh:71
Result< double > cardinality(cuda::stream_ref stream) const
Host-side cardinality estimate for this sketch, capped at added.
Definition sketch.cuh:361
static std::optional< double > ani(pairwise_summary const &summary) noexcept
Average nucleotide identity estimate from a raw pair summary.
Definition sketch.cuh:311
#define CUDDL_UNWRAP(expr)
Unwraps a cuddl::Result or throws std::runtime_error on failure (tests and apps).
Definition error.hpp:235
Batch operations over contiguous device rows.
Definition a48.hpp:14
cuda::std::unexpected< Error > Err(Error error)
Failure return; converts to any Result<T> via cuda::std::unexpected.
Definition error.hpp:176
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
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
Device-visible scratch for the owning cardinality wrapper.
Definition sketch.cuh:33
Persistent zero-copy scratch used by owning synchronous result wrappers.
Definition sketch.cuh:43
cardinality_scratch cardinality
Definition sketch.cuh:44
hybrid_cardinality_estimates hybrid
Definition sketch.cuh:45
pairwise_summary summary
Definition sketch.cuh:46
A span that is assumed to point to device-accessible memory.
Experimental DDL cardinality estimates computed from one register scan.
Pairwise counts with an optional compile-time cardinality field.