31 namespace seqan3::detail
59 template <std::ranges::view urng_t, simd::simd_concept simd_t>
60 class view_to_simd :
public std::ranges::view_interface<view_to_simd<urng_t, simd_t>>
64 static_assert(std::ranges::forward_range<urng_t>,
65 "The underlying range must model forward_range.");
66 static_assert(std::ranges::input_range<value_type_t<urng_t>>,
67 "Expects the value type of the underlying range to be an input_range.");
69 "Expects the inner range to be default constructible.");
71 "Expects semi-alphabet as value type of the inner range.");
76 using inner_range_type = value_type_t<urng_t>;
78 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 = std::ranges::contiguous_range<inner_range_type> &&
88 std::sized_sentinel_for<std::ranges::iterator_t<inner_range_type>,
89 std::ranges::sentinel_t<inner_range_type>> &&
90 sizeof(alphabet_rank_t<value_type_t<inner_range_type>>) == 1;
93 static constexpr uint8_t chunk_size = simd_traits<simd_t>::length;
95 static constexpr uint8_t chunks_per_load = simd_traits<simd_t>::max_length / chunk_size;
97 static constexpr uint8_t total_chunks = fast_load ? (chunks_per_load * chunks_per_load) : 1;
99 static constexpr
auto alphabet_size = alphabet_size<value_type_t<inner_range_type>>;
103 struct iterator_type;
110 constexpr view_to_simd() =
default;
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) :
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>
137 std::ranges::viewable_range<other_urng_t>
139 constexpr view_to_simd(other_urng_t && urng, scalar_type
const padding_value =
alphabet_size) :
147 constexpr iterator_type
begin() noexcept
154 constexpr
void begin() const noexcept = delete;
156 constexpr
void cbegin() const noexcept = delete;
159 constexpr
std::ranges::default_sentinel_t end() noexcept
161 return std::ranges::default_sentinel;
165 constexpr
void end() const noexcept = delete;
167 constexpr
void cend() const noexcept = delete;
176 constexpr
size_t size() const noexcept
178 requires
std::ranges::sized_range<inner_range_type>
181 auto it = std::ranges::max_element(urng, [] (
auto & lhs,
auto & rhs)
186 return (it != std::ranges::end(urng)) ? (
std::ranges::size(*it) + chunk_size - 1) / chunk_size : 0;
192 value_type_t<urng_t> empty_inner_range{};
194 simd_t padding_simd_vector{};
195 scalar_type padding_value{};
205 template <std::ranges::view urng_t, simd::simd_concept simd_t>
206 class view_to_simd<urng_t, simd_t>::iterator_type
213 using value_type = chunk_type;
214 using pointer = void;
215 using difference_type = ptrdiff_t;
217 using iterator_concept = iterator_category;
223 constexpr iterator_type() =
default;
224 constexpr iterator_type(iterator_type
const &) =
default;
225 constexpr iterator_type(iterator_type &&) =
default;
226 constexpr iterator_type & operator=(iterator_type
const &) =
default;
227 constexpr iterator_type & operator=(iterator_type &&) =
default;
228 ~iterator_type() =
default;
238 constexpr iterator_type(view_to_simd & this_view) : this_view{&this_view}, current_chunk_pos{0}
242 for (
auto it = std::ranges::begin(this_view.urng); it != std::ranges::end(this_view.urng); ++it, ++seq_id)
245 cached_sentinel[seq_id] = std::ranges::end(*it);
249 for (; seq_id < chunk_size; ++seq_id)
252 cached_sentinel[seq_id] = std::ranges::end(this_view.empty_inner_range);
255 final_chunk = all_iterators_reached_sentinel();
265 constexpr reference operator*() const noexcept
268 assert(this_view !=
nullptr);
269 return std::span{this_view->cached_simd_chunks[current_chunk_pos].
begin(),
270 (current_chunk_pos == final_chunk_pos) ? final_chunk_size : chunk_size};
277 constexpr iterator_type & operator++()
280 if constexpr (fast_load)
282 if (current_chunk_pos == final_chunk_pos)
285 current_chunk_pos = 0;
301 constexpr value_type operator++(
int )
303 value_type tmp = this->operator*();
312 constexpr
bool operator==(std::ranges::default_sentinel_t
const &)
const noexcept
319 friend constexpr
bool operator==(std::ranges::default_sentinel_t
const &, iterator_type
const & rhs) noexcept
325 constexpr
bool operator!=(std::ranges::default_sentinel_t
const &)
const noexcept
331 friend constexpr
bool operator!=(std::ranges::default_sentinel_t
const &, iterator_type
const & rhs) noexcept
348 auto unpack(max_simd_type
const & row)
const
350 if constexpr (chunk_size == simd_traits<max_simd_type>::length / 2)
352 return std::array{simd::upcast<simd_t>(extract_halve<0>(row)),
353 simd::upcast<simd_t>(extract_halve<1>(row))};
355 else if constexpr (chunk_size == simd_traits<max_simd_type>::length / 4)
357 return std::array{simd::upcast<simd_t>(extract_quarter<0>(row)),
358 simd::upcast<simd_t>(extract_quarter<1>(row)),
359 simd::upcast<simd_t>(extract_quarter<2>(row)),
360 simd::upcast<simd_t>(extract_quarter<3>(row))};
362 else if constexpr (chunk_size == simd_traits<max_simd_type>::length / 8)
364 return std::array{simd::upcast<simd_t>(extract_eighth<0>(row)),
365 simd::upcast<simd_t>(extract_eighth<1>(row)),
366 simd::upcast<simd_t>(extract_eighth<2>(row)),
367 simd::upcast<simd_t>(extract_eighth<3>(row)),
368 simd::upcast<simd_t>(extract_eighth<4>(row)),
369 simd::upcast<simd_t>(extract_eighth<5>(row)),
370 simd::upcast<simd_t>(extract_eighth<6>(row)),
371 simd::upcast<simd_t>(extract_eighth<7>(row))};
389 constexpr
void split_into_sub_matrices(
std::array<max_simd_type, simd_traits<max_simd_type>::length> matrix)
const
391 auto apply_padding = [
this] (simd_t
const vec)
393 return (vec == simd::fill<simd_t>(static_cast<uint8_t>(~0))) ? this_view->padding_simd_vector : vec;
397 for (uint8_t row = 0; row < static_cast<uint8_t>(matrix.size()); ++row)
400 auto chunked_row = unpack(matrix[row]);
402 if constexpr (chunked_row.size() == 1)
404 this_view->cached_simd_chunks[0][row] = apply_padding(
std::move(chunked_row[0]));
408 static_assert(chunked_row.size() == chunks_per_load,
"Expected chunks_per_load many simd vectors.");
410 for (uint8_t chunk = 0; chunk < chunks_per_load; ++chunk)
412 size_t idx = chunk * chunks_per_load + row / chunk_size;
413 this_view->cached_simd_chunks[idx][row % chunk_size] = apply_padding(
std::move(chunked_row[chunk]));
422 constexpr
bool all_iterators_reached_sentinel() const noexcept
426 return std::ranges::all_of(views::zip(cached_iter, cached_sentinel), [] (
auto && iterator_sentinel_pair)
428 return get<0>(iterator_sentinel_pair) == get<1>(iterator_sentinel_pair);
442 constexpr simd_t convert_single_column()
445 simd_t simd_column{};
446 for (
size_t idx = 0u; idx < chunk_size; ++idx)
448 if (cached_iter[idx] == cached_sentinel[idx])
450 simd_column[idx] = this_view->padding_value;
454 simd_column[idx] = static_cast<scalar_type>(
seqan3::to_rank(*cached_iter[idx]));
471 template <
typename array_t>
472 constexpr
void update_final_chunk_position(array_t
const & iterators_before_update) noexcept
474 size_t max_distance = 0;
475 for (
auto && [it, sent] : views::zip(iterators_before_update, cached_sentinel))
476 max_distance = std::max<size_t>(std::ranges::distance(it, sent), max_distance);
478 assert(max_distance > 0);
479 assert(max_distance <= (total_chunks * chunk_size));
482 final_chunk_pos = max_distance / chunk_size;
484 final_chunk_size = (max_distance % chunk_size) + 1;
488 constexpr
void underflow()
493 at_end = final_chunk;
519 constexpr int8_t max_size = simd_traits<simd_t>::max_length;
521 decltype(cached_iter) iterators_before_update{cached_iter};
523 for (uint8_t sequence_pos = 0; sequence_pos < chunk_size; ++sequence_pos)
525 for (uint8_t chunk_pos = 0; chunk_pos < chunks_per_load; ++chunk_pos)
527 uint8_t pos = chunk_pos * chunk_size + sequence_pos;
528 if (cached_sentinel[sequence_pos] - cached_iter[sequence_pos] >= max_size)
530 matrix[pos] = simd::load<max_simd_type>(
std::addressof(*cached_iter[sequence_pos]));
535 matrix[pos] = simd::fill<max_simd_type>(~0);
536 auto & sequence_it = cached_iter[sequence_pos];
537 for (int8_t idx = 0; sequence_it != cached_sentinel[sequence_pos]; ++sequence_it, ++idx)
544 final_chunk = all_iterators_reached_sentinel();
547 update_final_chunk_position(iterators_before_update);
549 simd::transpose(matrix);
550 split_into_sub_matrices(
std::move(matrix));
554 constexpr
void underflow()
559 at_end = final_chunk;
563 decltype(cached_iter) iterators_before_update{cached_iter};
564 for (
size_t i = 0; i < chunk_size; ++i)
565 this_view->cached_simd_chunks[0][i] = convert_single_column();
567 final_chunk = all_iterators_reached_sentinel();
570 update_final_chunk_position(iterators_before_update);
578 view_to_simd * this_view{
nullptr};
580 uint8_t final_chunk_size{chunk_size};
582 uint8_t final_chunk_pos{total_chunks - 1};
584 uint8_t current_chunk_pos{0};
586 bool final_chunk{
false};
603 template <simd::simd_concept simd_t>
607 using padding_t =
typename simd_traits<simd_t>::scalar_type;
612 constexpr
auto operator()(padding_t
const padding_value)
const noexcept
614 return detail::adaptor_from_functor{*
this, padding_value};
618 constexpr
auto operator()() const noexcept
620 return detail::adaptor_from_functor{*
this};
628 template <std::ranges::range urng_t>
629 constexpr
auto operator()(urng_t && urange, padding_t
const padding_value)
const noexcept
631 static_assert(std::ranges::forward_range<urng_t>,
632 "The underlying range in views::to_simd must model std::ranges::forward_range.");
633 static_assert(std::ranges::viewable_range<urng_t>,
634 "The underlying range in views::to_simd must model std::ranges::viewable_range.");
635 static_assert(std::ranges::input_range<value_type_t<urng_t>>,
636 "The value type of the underlying range must model std::ranges::input_range.");
638 "The value type of the inner ranges must model seqan3::semialphabet.");
640 return view_to_simd<type_reduce_view<urng_t>, simd_t>{std::forward<urng_t>(urange), padding_value};
647 template <std::ranges::range urng_t>
648 constexpr
auto operator()(urng_t && urange)
const noexcept
650 static_assert(std::ranges::forward_range<urng_t>,
651 "The underlying range in views::to_simd must model std::ranges::forward_range.");
652 static_assert(std::ranges::viewable_range<urng_t>,
653 "The underlying range in views::to_simd must model std::ranges::viewable_range.");
654 static_assert(std::ranges::input_range<value_type_t<urng_t>>,
655 "The value type of the underlying range must model std::ranges::input_range.");
657 "The value type of the inner ranges must model seqan3::semialphabet.");
659 return view_to_simd<type_reduce_view<urng_t>, simd_t>{std::forward<urng_t>(urange)};
663 template <std::ranges::range urng_t>
664 constexpr
friend auto operator|(urng_t && urange, to_simd_fn
const & me)
666 return me(std::forward<urng_t>(urange));
776 template <simd::simd_concept simd_t>
777 inline constexpr
auto to_simd = detail::to_simd_fn<simd_t>{};