30namespace seqan3::detail
58template <std::ranges::view urng_t, simd::simd_concept simd_t>
59class view_to_simd :
public std::ranges::view_interface<view_to_simd<urng_t, simd_t>>
62 static_assert(std::ranges::forward_range<urng_t>,
"The underlying range must model forward_range.");
63 static_assert(std::ranges::input_range<std::ranges::range_value_t<urng_t>>,
64 "Expects the value type of the underlying range to be an input_range.");
65 static_assert(std::default_initializable<std::ranges::iterator_t<std::ranges::range_value_t<urng_t>>>,
66 "Expects the inner range iterator to be default initializable.");
67 static_assert(std::default_initializable<std::ranges::sentinel_t<std::ranges::range_value_t<urng_t>>>,
68 "Expects the inner range sentinel to be default initializable.");
70 "Expects semi-alphabet as value type of the inner range.");
75 using inner_range_type = std::ranges::range_value_t<urng_t>;
77 using scalar_type =
typename simd_traits<simd_t>::scalar_type;
79 using max_simd_type = simd_type_t<uint8_t, simd_traits<simd_t>::max_length>;
86 static constexpr bool fast_load =
87 std::ranges::contiguous_range<inner_range_type>
88 && std::sized_sentinel_for<std::ranges::iterator_t<inner_range_type>, std::ranges::sentinel_t<inner_range_type>>
89 &&
sizeof(alphabet_rank_t<std::ranges::range_value_t<inner_range_type>>) == 1;
92 static constexpr uint8_t chunk_size = simd_traits<simd_t>::length;
94 static constexpr uint8_t chunks_per_load = simd_traits<simd_t>::max_length / chunk_size;
96 static constexpr uint8_t total_chunks = fast_load ? (chunks_per_load * chunks_per_load) : 1;
98 static constexpr auto alphabet_size = seqan3::alphabet_size<std::ranges::range_value_t<inner_range_type>>;
102 struct iterator_type;
108 constexpr view_to_simd()
109 requires std::default_initializable<urng_t>
111 constexpr view_to_simd(view_to_simd const &) = default;
112 constexpr view_to_simd(view_to_simd &&) = default;
113 constexpr view_to_simd & operator=(view_to_simd const &) = default;
114 constexpr view_to_simd & operator=(view_to_simd &&) = default;
115 ~view_to_simd() = default;
121 constexpr view_to_simd(urng_t urng, scalar_type const padding_value = alphabet_size) :
122 urng{std::move(urng)},
123 padding_simd_vector{simd::fill<simd_t>(padding_value)},
124 padding_value{padding_value}
127 if (std::ranges::distance(urng) > chunk_size)
128 throw std::invalid_argument{
"The size of the underlying range must be less than or equal to the size of "
129 "the given simd type!"};
133 template <
typename other_urng_t>
134 requires (!std::same_as<std::remove_cvref_t<other_urng_t>, view_to_simd>)
135 && (!std::same_as<other_urng_t, urng_t>) && std::ranges::viewable_range<other_urng_t>
136 constexpr view_to_simd(other_urng_t && urng, scalar_type
const padding_value = alphabet_size) :
145 constexpr iterator_type
begin() noexcept
151 constexpr void begin() const noexcept = delete;
154 constexpr
std::default_sentinel_t end() noexcept
156 return std::default_sentinel;
160 constexpr void end() const noexcept = delete;
164 constexpr
bool empty() const noexcept
165 requires
std::ranges::forward_range<inner_range_type>
170 return std::ranges::empty(rng);
180 constexpr size_t size() const noexcept
181 requires
std::ranges::sized_range<inner_range_type>
184 [](
auto & lhs,
auto & rhs)
186 return std::ranges::size(lhs) < std::ranges::size(rhs);
189 return (it != std::ranges::end(urng)) ? (std::ranges::size(*it) + chunk_size - 1) / chunk_size : 0;
195 simd_t padding_simd_vector{};
196 scalar_type padding_value{};
206template <std::ranges::view urng_t, simd::simd_concept simd_t>
207class view_to_simd<urng_t, simd_t>::iterator_type
214 using value_type = reference;
215 using pointer = void;
216 using difference_type = ptrdiff_t;
218 using iterator_concept = iterator_category;
224 constexpr iterator_type() =
default;
225 constexpr iterator_type(iterator_type
const &) =
default;
226 constexpr iterator_type(iterator_type &&) =
default;
227 constexpr iterator_type & operator=(iterator_type
const &) =
default;
228 constexpr iterator_type & operator=(iterator_type &&) =
default;
229 ~iterator_type() =
default;
239 constexpr iterator_type(view_to_simd & this_view) : this_view{&this_view}, current_chunk_pos{0}
243 for (
auto it =
std::ranges::begin(this_view.urng); it != std::ranges::end(this_view.urng); ++it, ++seq_id)
246 cached_sentinel[seq_id] = std::ranges::end(*it);
256 auto sentinel_it = std::ranges::next(cached_iter[0], cached_sentinel[0]);
257 for (; seq_id < chunk_size; ++seq_id)
259 cached_iter[seq_id] = sentinel_it;
260 cached_sentinel[seq_id] = cached_sentinel[0];
264 final_chunk = all_iterators_reached_sentinel();
275 constexpr reference operator*() const noexcept
277 assert(this_view !=
nullptr);
278 return std::span{this_view->cached_simd_chunks[current_chunk_pos].
begin(),
279 (current_chunk_pos == final_chunk_pos) ? final_chunk_size : chunk_size};
287 constexpr iterator_type & operator++()
289 if constexpr (fast_load)
291 if (current_chunk_pos == final_chunk_pos)
294 current_chunk_pos = 0;
310 constexpr value_type operator++(
int )
312 value_type tmp = this->operator*();
322 constexpr bool operator==(std::default_sentinel_t
const &)
const noexcept
328 friend constexpr bool operator==(std::default_sentinel_t
const &, iterator_type
const & rhs)
noexcept
334 constexpr bool operator!=(std::default_sentinel_t
const &)
const noexcept
340 friend constexpr bool operator!=(std::default_sentinel_t
const &, iterator_type
const & rhs)
noexcept
357 auto unpack(max_simd_type
const & row)
const
359 if constexpr (chunk_size == simd_traits<max_simd_type>::length / 2)
361 return std::array{simd::upcast<simd_t>(extract_half<0>(row)),
362 simd::upcast<simd_t>(extract_half<1>(row))};
364 else if constexpr (chunk_size == simd_traits<max_simd_type>::length / 4)
366 return std::array{simd::upcast<simd_t>(extract_quarter<0>(row)),
367 simd::upcast<simd_t>(extract_quarter<1>(row)),
368 simd::upcast<simd_t>(extract_quarter<2>(row)),
369 simd::upcast<simd_t>(extract_quarter<3>(row))};
371 else if constexpr (chunk_size == simd_traits<max_simd_type>::length / 8)
373 return std::array{simd::upcast<simd_t>(extract_eighth<0>(row)),
374 simd::upcast<simd_t>(extract_eighth<1>(row)),
375 simd::upcast<simd_t>(extract_eighth<2>(row)),
376 simd::upcast<simd_t>(extract_eighth<3>(row)),
377 simd::upcast<simd_t>(extract_eighth<4>(row)),
378 simd::upcast<simd_t>(extract_eighth<5>(row)),
379 simd::upcast<simd_t>(extract_eighth<6>(row)),
380 simd::upcast<simd_t>(extract_eighth<7>(row))};
398 constexpr void split_into_sub_matrices(
std::array<max_simd_type, simd_traits<max_simd_type>::length> matrix)
const
400 auto apply_padding = [
this](simd_t
const vec)
402 return (vec == simd::fill<simd_t>(
static_cast<uint8_t
>(~0))) ? this_view->padding_simd_vector : vec;
406 for (uint8_t row = 0; row < static_cast<uint8_t>(matrix.size()); ++row)
409 auto chunked_row = unpack(matrix[row]);
411 if constexpr (chunked_row.size() == 1)
413 this_view->cached_simd_chunks[0][row] = apply_padding(std::move(chunked_row[0]));
417 static_assert(chunked_row.size() == chunks_per_load,
"Expected chunks_per_load many simd vectors.");
419 for (uint8_t chunk = 0;
chunk < chunks_per_load; ++
chunk)
421 size_t idx =
chunk * chunks_per_load + row / chunk_size;
422 this_view->cached_simd_chunks[idx][row % chunk_size] = apply_padding(std::move(chunked_row[chunk]));
431 constexpr bool all_iterators_reached_sentinel() const noexcept
436 [](
auto && iterator_sentinel_pair)
438 return get<0>(iterator_sentinel_pair) == get<1>(iterator_sentinel_pair);
452 constexpr simd_t convert_single_column() noexcept
454 simd_t simd_column{};
455 for (
size_t idx = 0u; idx < chunk_size; ++idx)
457 if (cached_iter[idx] == cached_sentinel[idx])
459 simd_column[idx] = this_view->padding_value;
463 simd_column[idx] =
static_cast<scalar_type
>(
seqan3::to_rank(*cached_iter[idx]));
480 template <
typename array_t>
481 constexpr void update_final_chunk_position(array_t
const & iterators_before_update)
noexcept
483 size_t max_distance = 0;
484 for (
auto && [it, sent] :
views::zip(iterators_before_update, cached_sentinel))
485 max_distance = std::max<size_t>(std::ranges::distance(it, sent), max_distance);
487 assert(max_distance > 0);
488 assert(max_distance <= (total_chunks * chunk_size));
491 final_chunk_pos = max_distance / chunk_size;
493 final_chunk_size = (max_distance % chunk_size) + 1;
497 constexpr void underflow()
500 at_end = final_chunk;
526 constexpr int8_t max_size = simd_traits<simd_t>::max_length;
528 decltype(cached_iter) iterators_before_update{cached_iter};
530 for (uint8_t sequence_pos = 0; sequence_pos < chunk_size; ++sequence_pos)
532 for (uint8_t chunk_pos = 0; chunk_pos < chunks_per_load; ++chunk_pos)
534 uint8_t pos = chunk_pos * chunk_size + sequence_pos;
535 if (cached_sentinel[sequence_pos] - cached_iter[sequence_pos] >= max_size)
537 matrix[pos] = simd::load<max_simd_type>(
std::addressof(*cached_iter[sequence_pos]));
542 matrix[pos] = simd::fill<max_simd_type>(~0);
543 auto & sequence_it = cached_iter[sequence_pos];
544 for (int8_t idx = 0; sequence_it != cached_sentinel[sequence_pos]; ++sequence_it, ++idx)
551 final_chunk = all_iterators_reached_sentinel();
554 update_final_chunk_position(iterators_before_update);
556 simd::transpose(matrix);
557 split_into_sub_matrices(std::move(matrix));
561 constexpr void underflow()
562 requires (!fast_load)
564 at_end = final_chunk;
568 decltype(cached_iter) iterators_before_update{cached_iter};
569 for (
size_t i = 0; i < chunk_size; ++i)
570 this_view->cached_simd_chunks[0][i] = convert_single_column();
572 final_chunk = all_iterators_reached_sentinel();
575 update_final_chunk_position(iterators_before_update);
583 view_to_simd * this_view{
nullptr};
585 uint8_t final_chunk_size{chunk_size};
587 uint8_t final_chunk_pos{total_chunks - 1};
589 uint8_t current_chunk_pos{0};
591 bool final_chunk{
true};
608template <simd::simd_concept simd_t>
612 using padding_t =
typename simd_traits<simd_t>::scalar_type;
617 constexpr auto operator()(padding_t
const padding_value)
const noexcept
619 return detail::adaptor_from_functor{*
this, padding_value};
623 constexpr auto operator()() const noexcept
625 return detail::adaptor_from_functor{*
this};
633 template <std::ranges::range urng_t>
634 constexpr auto operator()(urng_t && urange, padding_t
const padding_value)
const noexcept
636 static_assert(std::ranges::forward_range<urng_t>,
637 "The underlying range in views::to_simd must model std::ranges::forward_range.");
638 static_assert(std::ranges::viewable_range<urng_t>,
639 "The underlying range in views::to_simd must model std::ranges::viewable_range.");
640 static_assert(std::ranges::input_range<std::ranges::range_value_t<urng_t>>,
641 "The value type of the underlying range must model std::ranges::input_range.");
643 "The value type of the inner ranges must model seqan3::semialphabet.");
645 return view_to_simd<type_reduce_t<urng_t>, simd_t>{std::forward<urng_t>(urange), padding_value};
652 template <std::ranges::range urng_t>
653 constexpr auto operator()(urng_t && urange)
const noexcept
655 static_assert(std::ranges::forward_range<urng_t>,
656 "The underlying range in views::to_simd must model std::ranges::forward_range.");
657 static_assert(std::ranges::viewable_range<urng_t>,
658 "The underlying range in views::to_simd must model std::ranges::viewable_range.");
659 static_assert(std::ranges::input_range<std::ranges::range_value_t<urng_t>>,
660 "The value type of the underlying range must model std::ranges::input_range.");
662 "The value type of the inner ranges must model seqan3::semialphabet.");
664 return view_to_simd<type_reduce_t<urng_t>, simd_t>{std::forward<urng_t>(urange)};
668 template <std::ranges::range urng_t>
669 constexpr friend auto operator|(urng_t && urange, to_simd_fn
const & me)
671 return me(std::forward<urng_t>(urange));
781template <simd::simd_concept simd_t>
782inline constexpr auto to_simd = detail::to_simd_fn<simd_t>{};
Provides seqan3::detail::adaptor_from_functor.
Provides algorithms to modify seqan3::simd::simd_type.
Core alphabet concept and free function/type trait wrappers.
Provides various transformation traits used by the range module.
constexpr auto alphabet_size
A type trait that holds the size of a (semi-)alphabet.
Definition: alphabet/concept.hpp:849
constexpr auto to_rank
Return the rank representation of a (semi-)alphabet object.
Definition: alphabet/concept.hpp:155
auto operator|(validator1_type &&vali1, validator2_type &&vali2)
Enables the chaining of validators.
Definition: validators.hpp:1124
constexpr size_t size
The size of a type pack.
Definition: type_pack/traits.hpp:146
seqan::std::views::chunk chunk
A view adaptor that divides a range into chunks. <dl class="no-api">This entity is not part of the Se...
Definition: chunk.hpp:26
seqan::std::views::zip zip
A view adaptor that takes several views and returns tuple-like values from every i-th element of each...
Definition: zip.hpp:27
constexpr auto type_reduce
A view adaptor that behaves like std::views::all, but type erases certain ranges.
Definition: type_reduce.hpp:150
The basis for seqan3::alphabet, but requires only rank interface (not char).
The SeqAn namespace for views.
Definition: char_strictly_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:782
SeqAn specific customisations in the standard namespace.
Provides seqan3::simd::simd_type.
Provides seqan3::simd::simd_traits.
Provides type traits for working with templates.
Provides seqan3::views::type_reduce.
Provides seqan3::simd::simd_concept.
Provides seqan3::views::zip.