29 namespace seqan3::detail
57 template <std::ranges::view urng_t, simd::simd_concept simd_t>
58 class view_to_simd :
public std::ranges::view_interface<view_to_simd<urng_t, simd_t>>
62 static_assert(std::ranges::forward_range<urng_t>,
63 "The underlying range must model forward_range.");
64 static_assert(std::ranges::input_range<std::ranges::range_value_t<urng_t>>,
65 "Expects the value type of the underlying range to be an input_range.");
66 static_assert(std::default_initializable<std::ranges::range_value_t<urng_t>>,
67 "Expects the inner range to be default constructible.");
68 static_assert(
semialphabet<std::ranges::range_value_t<std::ranges::range_value_t<urng_t>>>,
69 "Expects semi-alphabet as value type of the inner range.");
74 using inner_range_type = std::ranges::range_value_t<urng_t>;
76 using scalar_type =
typename simd_traits<simd_t>::scalar_type;
78 using max_simd_type = simd_type_t<uint8_t, simd_traits<simd_t>::max_length>;
85 static constexpr
bool fast_load = std::ranges::contiguous_range<inner_range_type> &&
86 std::sized_sentinel_for<std::ranges::iterator_t<inner_range_type>,
87 std::ranges::sentinel_t<inner_range_type>> &&
88 sizeof(alphabet_rank_t<std::ranges::range_value_t<inner_range_type>>) == 1;
91 static constexpr uint8_t chunk_size = simd_traits<simd_t>::length;
93 static constexpr uint8_t chunks_per_load = simd_traits<simd_t>::max_length / chunk_size;
95 static constexpr uint8_t total_chunks = fast_load ? (chunks_per_load * chunks_per_load) : 1;
97 static constexpr
auto alphabet_size = alphabet_size<std::ranges::range_value_t<inner_range_type>>;
101 struct iterator_type;
108 constexpr view_to_simd() =
default;
109 constexpr view_to_simd(view_to_simd
const &) =
default;
110 constexpr view_to_simd(view_to_simd &&) =
default;
111 constexpr view_to_simd & operator=(view_to_simd
const &) =
default;
112 constexpr view_to_simd & operator=(view_to_simd &&) =
default;
113 ~view_to_simd() =
default;
119 constexpr view_to_simd(urng_t urng, scalar_type
const padding_value =
alphabet_size) :
121 padding_simd_vector{simd::
fill<simd_t>(padding_value)},
122 padding_value{padding_value}
125 if (std::ranges::distance(urng) > chunk_size)
126 throw std::invalid_argument{
"The size of the underlying range must be less than or equal to the size of "
127 "the given simd type!"};
131 template <
typename other_urng_t>
134 (!std::same_as<other_urng_t, urng_t>) &&
135 std::ranges::viewable_range<other_urng_t>
137 constexpr view_to_simd(other_urng_t && urng, scalar_type
const padding_value =
alphabet_size) :
146 constexpr iterator_type
begin() noexcept
152 constexpr
void begin() const noexcept = delete;
155 constexpr
std::default_sentinel_t end() noexcept
157 return std::default_sentinel;
161 constexpr
void end() const noexcept = delete;
165 constexpr
bool empty() const noexcept
167 requires
std::ranges::forward_range<inner_range_type>
170 return std::ranges::all_of(urng, [] (
auto & rng)
172 return std::ranges::empty(rng);
182 constexpr
size_t size() const noexcept
184 requires
std::ranges::sized_range<inner_range_type>
187 auto it = std::ranges::max_element(urng, [] (
auto & lhs,
auto & rhs)
192 return (it != std::ranges::end(urng)) ? (
std::ranges::size(*it) + chunk_size - 1) / chunk_size : 0;
199 simd_t padding_simd_vector{};
200 scalar_type padding_value{};
210 template <std::ranges::view urng_t, simd::simd_concept simd_t>
211 class view_to_simd<urng_t, simd_t>::iterator_type
218 using value_type = reference;
219 using pointer = void;
220 using difference_type = ptrdiff_t;
222 using iterator_concept = iterator_category;
228 constexpr iterator_type() =
default;
229 constexpr iterator_type(iterator_type
const &) =
default;
230 constexpr iterator_type(iterator_type &&) =
default;
231 constexpr iterator_type & operator=(iterator_type
const &) =
default;
232 constexpr iterator_type & operator=(iterator_type &&) =
default;
233 ~iterator_type() =
default;
243 constexpr iterator_type(view_to_simd & this_view) : this_view{&this_view}, current_chunk_pos{0}
247 for (
auto it = std::ranges::begin(this_view.urng); it != std::ranges::end(this_view.urng); ++it, ++seq_id)
250 cached_sentinel[seq_id] = std::ranges::end(*it);
260 auto sentinel_it = std::ranges::next(cached_iter[0], cached_sentinel[0]);
261 for (; seq_id < chunk_size; ++seq_id)
263 cached_iter[seq_id] = sentinel_it;
264 cached_sentinel[seq_id] = cached_sentinel[0];
268 final_chunk = all_iterators_reached_sentinel();
279 constexpr reference operator*() const noexcept
281 assert(this_view !=
nullptr);
282 return std::span{this_view->cached_simd_chunks[current_chunk_pos].begin(),
283 (current_chunk_pos == final_chunk_pos) ? final_chunk_size : chunk_size};
291 constexpr iterator_type & operator++()
293 if constexpr (fast_load)
295 if (current_chunk_pos == final_chunk_pos)
298 current_chunk_pos = 0;
314 constexpr value_type operator++(
int )
316 value_type tmp = this->operator*();
326 constexpr
bool operator==(std::default_sentinel_t
const &)
const noexcept
332 friend constexpr
bool operator==(std::default_sentinel_t
const &, iterator_type
const & rhs) noexcept
338 constexpr
bool operator!=(std::default_sentinel_t
const &)
const noexcept
344 friend constexpr
bool operator!=(std::default_sentinel_t
const &, iterator_type
const & rhs) noexcept
361 auto unpack(max_simd_type
const & row)
const
363 if constexpr (chunk_size == simd_traits<max_simd_type>::length / 2)
365 return std::array{simd::upcast<simd_t>(extract_half<0>(row)),
366 simd::upcast<simd_t>(extract_half<1>(row))};
368 else if constexpr (chunk_size == simd_traits<max_simd_type>::length / 4)
370 return std::array{simd::upcast<simd_t>(extract_quarter<0>(row)),
371 simd::upcast<simd_t>(extract_quarter<1>(row)),
372 simd::upcast<simd_t>(extract_quarter<2>(row)),
373 simd::upcast<simd_t>(extract_quarter<3>(row))};
375 else if constexpr (chunk_size == simd_traits<max_simd_type>::length / 8)
377 return std::array{simd::upcast<simd_t>(extract_eighth<0>(row)),
378 simd::upcast<simd_t>(extract_eighth<1>(row)),
379 simd::upcast<simd_t>(extract_eighth<2>(row)),
380 simd::upcast<simd_t>(extract_eighth<3>(row)),
381 simd::upcast<simd_t>(extract_eighth<4>(row)),
382 simd::upcast<simd_t>(extract_eighth<5>(row)),
383 simd::upcast<simd_t>(extract_eighth<6>(row)),
384 simd::upcast<simd_t>(extract_eighth<7>(row))};
402 constexpr
void split_into_sub_matrices(
std::array<max_simd_type, simd_traits<max_simd_type>::length> matrix)
const
404 auto apply_padding = [
this] (simd_t
const vec)
406 return (vec == simd::fill<simd_t>(
static_cast<uint8_t
>(~0))) ? this_view->padding_simd_vector : vec;
410 for (uint8_t row = 0; row < static_cast<uint8_t>(matrix.size()); ++row)
413 auto chunked_row = unpack(matrix[row]);
415 if constexpr (chunked_row.size() == 1)
417 this_view->cached_simd_chunks[0][row] = apply_padding(
std::move(chunked_row[0]));
421 static_assert(chunked_row.size() == chunks_per_load,
"Expected chunks_per_load many simd vectors.");
425 size_t idx =
chunk * chunks_per_load + row / chunk_size;
426 this_view->cached_simd_chunks[idx][row % chunk_size] = apply_padding(
std::move(chunked_row[
chunk]));
435 constexpr
bool all_iterators_reached_sentinel() const noexcept
439 return std::ranges::all_of(
views::zip(cached_iter, cached_sentinel), [] (
auto && iterator_sentinel_pair)
441 return get<0>(iterator_sentinel_pair) == get<1>(iterator_sentinel_pair);
455 constexpr simd_t convert_single_column()
458 simd_t simd_column{};
459 for (
size_t idx = 0u; idx < chunk_size; ++idx)
461 if (cached_iter[idx] == cached_sentinel[idx])
463 simd_column[idx] = this_view->padding_value;
467 simd_column[idx] =
static_cast<scalar_type
>(
seqan3::to_rank(*cached_iter[idx]));
484 template <
typename array_t>
485 constexpr
void update_final_chunk_position(array_t
const & iterators_before_update) noexcept
487 size_t max_distance = 0;
488 for (
auto && [it, sent] :
views::zip(iterators_before_update, cached_sentinel))
489 max_distance = std::max<size_t>(std::ranges::distance(it, sent), max_distance);
491 assert(max_distance > 0);
492 assert(max_distance <= (total_chunks * chunk_size));
495 final_chunk_pos = max_distance / chunk_size;
497 final_chunk_size = (max_distance % chunk_size) + 1;
501 constexpr
void underflow()
506 at_end = final_chunk;
532 constexpr int8_t max_size = simd_traits<simd_t>::max_length;
534 decltype(cached_iter) iterators_before_update{cached_iter};
536 for (uint8_t sequence_pos = 0; sequence_pos < chunk_size; ++sequence_pos)
538 for (uint8_t chunk_pos = 0; chunk_pos < chunks_per_load; ++chunk_pos)
540 uint8_t pos = chunk_pos * chunk_size + sequence_pos;
541 if (cached_sentinel[sequence_pos] - cached_iter[sequence_pos] >= max_size)
543 matrix[pos] = simd::load<max_simd_type>(
std::addressof(*cached_iter[sequence_pos]));
548 matrix[pos] = simd::fill<max_simd_type>(~0);
549 auto & sequence_it = cached_iter[sequence_pos];
550 for (int8_t idx = 0; sequence_it != cached_sentinel[sequence_pos]; ++sequence_it, ++idx)
557 final_chunk = all_iterators_reached_sentinel();
560 update_final_chunk_position(iterators_before_update);
562 simd::transpose(matrix);
563 split_into_sub_matrices(
std::move(matrix));
567 constexpr
void underflow()
569 requires (!fast_load)
572 at_end = final_chunk;
576 decltype(cached_iter) iterators_before_update{cached_iter};
577 for (
size_t i = 0; i < chunk_size; ++i)
578 this_view->cached_simd_chunks[0][i] = convert_single_column();
580 final_chunk = all_iterators_reached_sentinel();
583 update_final_chunk_position(iterators_before_update);
591 view_to_simd * this_view{
nullptr};
593 uint8_t final_chunk_size{chunk_size};
595 uint8_t final_chunk_pos{total_chunks - 1};
597 uint8_t current_chunk_pos{0};
599 bool final_chunk{
true};
616 template <simd::simd_concept simd_t>
620 using padding_t =
typename simd_traits<simd_t>::scalar_type;
625 constexpr
auto operator()(padding_t
const padding_value)
const noexcept
627 return detail::adaptor_from_functor{*
this, padding_value};
631 constexpr
auto operator()() const noexcept
633 return detail::adaptor_from_functor{*
this};
641 template <std::ranges::range urng_t>
642 constexpr
auto operator()(urng_t && urange, padding_t
const padding_value)
const noexcept
644 static_assert(std::ranges::forward_range<urng_t>,
645 "The underlying range in views::to_simd must model std::ranges::forward_range.");
646 static_assert(std::ranges::viewable_range<urng_t>,
647 "The underlying range in views::to_simd must model std::ranges::viewable_range.");
648 static_assert(std::ranges::input_range<std::ranges::range_value_t<urng_t>>,
649 "The value type of the underlying range must model std::ranges::input_range.");
650 static_assert(
semialphabet<std::ranges::range_value_t<std::ranges::range_value_t<urng_t>>>,
651 "The value type of the inner ranges must model seqan3::semialphabet.");
653 return view_to_simd<type_reduce_view<urng_t>, simd_t>{std::forward<urng_t>(urange), padding_value};
660 template <std::ranges::range urng_t>
661 constexpr
auto operator()(urng_t && urange)
const noexcept
663 static_assert(std::ranges::forward_range<urng_t>,
664 "The underlying range in views::to_simd must model std::ranges::forward_range.");
665 static_assert(std::ranges::viewable_range<urng_t>,
666 "The underlying range in views::to_simd must model std::ranges::viewable_range.");
667 static_assert(std::ranges::input_range<std::ranges::range_value_t<urng_t>>,
668 "The value type of the underlying range must model std::ranges::input_range.");
669 static_assert(
semialphabet<std::ranges::range_value_t<std::ranges::range_value_t<urng_t>>>,
670 "The value type of the inner ranges must model seqan3::semialphabet.");
672 return view_to_simd<type_reduce_view<urng_t>, simd_t>{std::forward<urng_t>(urange)};
676 template <std::ranges::range urng_t>
677 constexpr
friend auto operator|(urng_t && urange, to_simd_fn
const & me)
679 return me(std::forward<urng_t>(urange));
789 template <simd::simd_concept simd_t>
790 inline constexpr
auto to_simd = detail::to_simd_fn<simd_t>{};
Provides algorithms to modify seqan3::simd::simd_type.
Adaptations of algorithms from the Ranges TS.
Provides various transformation traits used by the range module.
Provides type traits for working with templates.
constexpr auto alphabet_size
A type trait that holds the size of a (semi-)alphabet.
Definition: concept.hpp:858
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition: concept.hpp:155
auto operator|(validator1_type &&vali1, validator2_type &&vali2)
Enables the chaining of validators.
Definition: validators.hpp:1104
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:150
constexpr auto chunk
A chunk view.
Definition: chunk.hpp:29
auto const get
A view calling std::get on each element in a range.
Definition: get.hpp:66
constexpr auto zip
A zip view.
Definition: zip.hpp:29
auto const move
A view that turns lvalue-references into rvalue-references.
Definition: move.hpp:70
constexpr auto type_reduce
A view adaptor that behaves like std::views::all, but type erases certain ranges.
Definition: type_reduce.hpp:158
The basis for seqan3::alphabet, but requires only rank interface (not char).
Provides C++20 additions to the <iterator> header.
The SeqAn namespace for views.
Definition: char_to.hpp:22
constexpr auto to_simd
A view that transforms a range of ranges into chunks of seqan3::simd vectors.
Definition: to_simd.hpp:790
SeqAn specific customisations in the standard namespace.
Provides algorithms for meta programming, parameter packs and seqan3::type_list.
Adaptations of concepts from the Ranges TS.
Provides seqan3::views::type_reduce.
Provides seqan3::simd::simd_concept.
Provides seqan3::simd::simd_type.
Provides seqan3::simd::simd_traits.
Provides seqan3::views::zip.