3#include <cub/version.cuh>
5#include <cuda/memory_pool>
6#include <cuda/std/cstdint>
25 CUB_VERSION == 300600,
26 "cuDDL requires CUB 3.6.0 from CCCL revision 486de1c44daf7d1a343cbbf0f5477e9bea8ad600; "
27 "use the Meson cccl subproject."
62template <u
int32_t K,
size_t BucketCount,
typename Layout = default_register_layout>
72 cuda::stream_ref stream,
73 std::optional<std::shared_ptr<device_blacklist const>> blacklist = std::nullopt
75 : blacklist_(std::move(blacklist)),
86 detail::device_pinned_pool(stream.device(), detail::pinned_placement::device_node),
91 if (!*blacklist_)
throw std::invalid_argument(
"blacklist owner must not be null");
92 CUDDL_UNWRAP((*blacklist_)->validate(K, BucketCount, stream));
108 added_ = std::nullopt;
109 return {storage_.data(), BucketCount};
114 return {storage_.data(), BucketCount};
134 [[nodiscard]] std::optional<uint64_t>
added() const noexcept {
143 if (
auto const result = view().
clear_async(stream); !result) {
152 if (
auto const result =
clear_async(stream); !result) {
155 return cuda_try([&] { stream.sync(); });
167 cuda::stream_ref stream,
170 if (source_blacklist.identity() != blacklist_identity() ||
171 (!source_blacklist.keys().empty() && source_blacklist.kmer_length() != K)) {
174 if (registers.size() != BucketCount) {
177 auto*
const destination = storage_.data();
178 if (
auto const result =
cuda_try([&] {
181 cuda::std::span{registers.data(), registers.size()},
182 cuda::std::span{destination, BucketCount}
188 added_ = std::nullopt;
201 if (
auto const result = view().
add_async(input, stream); !result) {
204 if (added_.has_value()) {
205 *added_ += input.size();
214 if (
auto const result =
add_async(input, stream); !result) {
217 return cuda_try([&] { stream.sync(); });
235 if (added_.has_value() && sequence.size() >= K) {
236 *added_ += sequence.size() - K + 1;
251 return cuda_try([&] { stream.sync(); });
255 template <
bool IncludeCardinality = false>
259 cuda::stream_ref stream
261 if (blacklist_identity() != other.blacklist_identity()) {
264 return view().template summary_async<IncludeCardinality>(other.view(), output, stream);
271 cuda::stream_ref stream
277 template <
bool IncludeCardinality = false>
280 auto*
const output = &mapped_scratch_.data()->summary;
281 if (
auto const result = summary_async<IncludeCardinality>(other, *output, stream);
283 return Err(result.error());
285 if (
auto const result =
cuda_try([&] { stream.sync(); }); !result) {
286 return Err(result.error());
288 return mapped_scratch_.data()->summary;
302 auto const equal =
summary.counts.equal;
303 auto const divisor = equal + std::min(
summary.counts.lower,
summary.counts.higher);
307 return static_cast<double>(equal) /
static_cast<double>(std::max(divisor, 6U));
316 return std::pow(*identity, 1.0 /
static_cast<double>(K));
325 auto const divisor =
summary.counts.equal +
summary.counts.higher;
329 return static_cast<double>(
summary.counts.equal) /
static_cast<double>(divisor);
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);
354 double* estimate_out,
355 cuda::stream_ref stream
362 auto*
const output = &mapped_scratch_.data()->cardinality;
363 if (
auto const result = view().
cardinality_async(&output->empty, &output->estimate, stream);
365 return Err(result.error());
367 if (
auto const result =
cuda_try([&] { stream.sync(); }); !result) {
368 return Err(result.error());
370 return capped(mapped_scratch_.data()->cardinality.estimate);
378 cuda::stream_ref stream
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());
384 if (
auto const result =
cuda_try([&] { stream.sync(); }); !result) {
385 return Err(result.error());
387 auto estimates = mapped_scratch_.data()->hybrid;
388 estimates.bbtools = capped(estimates.bbtools);
389 estimates.paper = capped(estimates.paper);
396 [[nodiscard]] view_type view() const noexcept {
398 {storage_.data(), BucketCount}, blacklist_ ? (*blacklist_)->view() :
cuda::std::nullopt
403 [[nodiscard]]
double capped(
double estimate)
const noexcept {
404 return added_.has_value() ? std::min(estimate,
static_cast<double>(*added_)) : estimate;
407 [[nodiscard]] uint64_t blacklist_identity() const noexcept {
408 return blacklist_ ? (*blacklist_)->source().identity() : 0;
411 std::optional<std::shared_ptr<device_blacklist const>> blacklist_;
412 mutable cuda::device_buffer<register_type> storage_;
414 buffer<detail::sketch_scratch, cuda::mr::host_accessible, cuda::mr::device_accessible>
418 mutable std::optional<uint64_t> added_ = 0;
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.
Move-only owning DDL sketch backed by one contiguous device allocation.
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.
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.
Result< pairwise_summary > summary(sketch const &other, cuda::stream_ref stream) const
Host-side fused pairwise summary (uses mapped scratch, synchronises).
static std::optional< double > containment(pairwise_summary const &summary) noexcept
Fraction of the left sketch's content shared with the right sketch.
static constexpr uint32_t kmer_length() noexcept
Compile-time k-mer length.
static std::optional< double > completeness(pairwise_summary const &summary) noexcept
Relative cardinality of the left sketch to the right sketch, clamped to [0, 1].
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.
static std::optional< double > wkid(pairwise_summary const &summary) noexcept
Weighted k-mer identity from a raw pair summary.
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.
std::optional< uint64_t > added() const noexcept
k-mers offered through this sketch's own add paths since the last clear.
Result< hybrid_cardinality_estimates > hybrid_cardinality(cuda::stream_ref stream) const
Experimental BBTools and paper-style HybridDDL estimates for comparison.
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.
static constexpr size_t bucket_count() noexcept
Number of registers in the sketch.
Result< pairwise_summary > compare(sketch const &other, cuda::stream_ref stream) const
Raw pairwise counts against other (host result).
device_span< register_type > data() noexcept
Device registers.
Result< void > clear_async(cuda::stream_ref stream) const noexcept
Resets every register to zero without synchronising.
sketch & operator=(sketch const &)=delete
Result< void > clear(cuda::stream_ref stream) const noexcept
Resets every register to zero, then synchronises.
Result< void > add_sequence_async(device_span< char const > sequence, cuda::stream_ref stream) const noexcept
Adds device-resident raw ASCII bases without clearing.
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.
device_span< register_type const > data() const noexcept
Device registers.
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.
sketch(cuda::stream_ref stream, std::optional< std::shared_ptr< device_blacklist const > > blacklist=std::nullopt)
Allocates and clears the sketch.
Result< double > cardinality(cuda::stream_ref stream) const
Host-side cardinality estimate for this sketch, capped at added.
static std::optional< double > ani(pairwise_summary const &summary) noexcept
Average nucleotide identity estimate from a raw pair summary.
#define CUDDL_UNWRAP(expr)
Unwraps a cuddl::Result or throws std::runtime_error on failure (tests and apps).
Batch operations over contiguous device rows.
cuda::std::unexpected< Error > Err(Error error)
Failure return; converts to any Result<T> via cuda::std::unexpected.
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.
static Error invalid_argument(std::string message)
Fallible API result: cuda::std::expected<T, Error> with cuDDL factories.
Device-visible scratch for the owning cardinality wrapper.
Persistent zero-copy scratch used by owning synchronous result wrappers.
cardinality_scratch cardinality
hybrid_cardinality_estimates hybrid
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.