SeqAn3 3.1.0
The Modern C++ library for sequence analysis.
algorithm.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
4// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6// -----------------------------------------------------------------------------------------------------
7
13#pragma once
14
15#include <array>
16#include <cassert>
17#include <seqan3/std/concepts>
18#include <utility>
19
26
27namespace seqan3::detail
28{
29
32template <simd::simd_concept simd_t, size_t... I>
33constexpr simd_t fill_impl(typename simd_traits<simd_t>::scalar_type const scalar, std::index_sequence<I...>) noexcept
34{
35 return simd_t{((void)I, scalar)...};
36}
37
40template <simd::simd_concept simd_t, typename scalar_t, scalar_t... I>
41constexpr simd_t iota_impl(scalar_t const offset, std::integer_sequence<scalar_t, I...>)
42{
43 return simd_t{static_cast<scalar_t>(offset + I)...};
44}
45
60template <size_t divisor, simd_concept simd_t>
61constexpr simd_t extract_impl(simd_t const & src, uint8_t const mask)
62{
63 simd_t dst{};
64 constexpr size_t chunk = simd_traits<simd_t>::length / divisor;
65 size_t offset = chunk * mask;
66 for (size_t i = 0; i < chunk; ++i)
67 dst[i] = src[i + offset];
68
69 return dst;
70}
71
80template <simd::simd_concept target_simd_t, simd::simd_concept source_simd_t>
81constexpr target_simd_t upcast_signed(source_simd_t const & src)
82{
83 static_assert(simd_traits<target_simd_t>::max_length == simd_traits<source_simd_t>::max_length,
84 "Target vector has different byte size.");
85
86 if constexpr (simd_traits<source_simd_t>::max_length == 16) // SSE4
87 return upcast_signed_sse4<target_simd_t>(src);
88 else if constexpr (simd_traits<source_simd_t>::max_length == 32) // AVX2
89 return upcast_signed_avx2<target_simd_t>(src);
90 else if constexpr (simd_traits<source_simd_t>::max_length == 64) // AVX512
91 return upcast_signed_avx512<target_simd_t>(src);
92 else
93 static_assert(simd_traits<source_simd_t>::max_length <= 32, "simd type is not supported.");
94}
95
104template <simd::simd_concept target_simd_t, simd::simd_concept source_simd_t>
105constexpr target_simd_t upcast_unsigned(source_simd_t const & src)
106{
107 static_assert(simd_traits<target_simd_t>::max_length == simd_traits<source_simd_t>::max_length,
108 "Target vector has different byte size.");
109
110 if constexpr (simd_traits<source_simd_t>::max_length == 16) // SSE4
111 return upcast_unsigned_sse4<target_simd_t>(src);
112 else if constexpr (simd_traits<source_simd_t>::max_length == 32) // AVX2
113 return upcast_unsigned_avx2<target_simd_t>(src);
114 else if constexpr (simd_traits<source_simd_t>::max_length == 64) // AVX512
115 return upcast_unsigned_avx512<target_simd_t>(src);
116 else
117 static_assert(simd_traits<source_simd_t>::max_length <= 32, "simd type is not supported.");
118}
119
142template <uint8_t index, simd::simd_concept simd_t>
143constexpr simd_t extract_half(simd_t const & src)
144{
145 static_assert(index < 2, "The index must be in the range of [0, 1]");
146
147 return detail::extract_impl<2>(src, index);
148}
149
151template <uint8_t index, simd::simd_concept simd_t>
152 requires detail::is_builtin_simd_v<simd_t> &&
153 detail::is_native_builtin_simd_v<simd_t>
154constexpr simd_t extract_half(simd_t const & src)
155{
156 static_assert(index < 2, "The index must be in the range of [0, 1]");
157
158 if constexpr (simd_traits<simd_t>::length < 2) // In case there are less elements available return unchanged value.
159 return src;
160 else if constexpr (simd_traits<simd_t>::max_length == 16) // SSE4
161 return detail::extract_half_sse4<index>(src);
162 else if constexpr (simd_traits<simd_t>::max_length == 32) // AVX2
163 return detail::extract_half_avx2<index>(src);
164 else // Anything else
165 return detail::extract_impl<2>(src, index);
166}
168
191template <uint8_t index, simd::simd_concept simd_t>
192constexpr simd_t extract_quarter(simd_t const & src)
193{
194 static_assert(index < 4, "The index must be in the range of [0, 1, 2, 3]");
195
196 return detail::extract_impl<4>(src, index);
197}
198
200template <uint8_t index, simd::simd_concept simd_t>
201 requires detail::is_builtin_simd_v<simd_t> &&
202 detail::is_native_builtin_simd_v<simd_t>
203constexpr simd_t extract_quarter(simd_t const & src)
204{
205 static_assert(index < 4, "The index must be in the range of [0, 1, 2, 3]");
206
207 if constexpr (simd_traits<simd_t>::length < 4) // In case there are less elements available return unchanged value.
208 return src;
209 else if constexpr (simd_traits<simd_t>::max_length == 16) // SSE4
210 return detail::extract_quarter_sse4<index>(src);
211 else if constexpr (simd_traits<simd_t>::max_length == 32) // AVX2
212 return detail::extract_quarter_avx2<index>(src);
213 else // Anything else
214 return detail::extract_impl<4>(src, index);
215}
217
240template <uint8_t index, simd::simd_concept simd_t>
241constexpr simd_t extract_eighth(simd_t const & src)
242{
243 return detail::extract_impl<8>(src, index);
244}
245
247template <uint8_t index, simd::simd_concept simd_t>
248 requires detail::is_builtin_simd_v<simd_t> &&
249 detail::is_native_builtin_simd_v<simd_t>
250constexpr simd_t extract_eighth(simd_t const & src)
251{
252 static_assert(index < 8, "The index must be in the range of [0, 1, 2, 3, 4, 5, 6, 7]");
253
254 if constexpr (simd_traits<simd_t>::length < 8) // In case there are less elements available return unchanged value.
255 return src;
256 else if constexpr (simd_traits<simd_t>::max_length == 16) // SSE4
257 return detail::extract_eighth_sse4<index>(src);
258 else if constexpr (simd_traits<simd_t>::max_length == 32) // AVX2
259 return detail::extract_eighth_avx2<index>(src);
260 else // Anything else
261 return detail::extract_impl<8>(src, index);
262}
264
265} // namespace seqan3::detail
266
267namespace seqan3
268{
269
270inline namespace simd
271{
272
282template <simd::simd_concept simd_t>
283constexpr simd_t fill(typename simd_traits<simd_t>::scalar_type const scalar) noexcept
284{
285 constexpr size_t length = simd_traits<simd_t>::length;
286 return detail::fill_impl<simd_t>(scalar, std::make_index_sequence<length>{});
287}
288
298template <simd::simd_concept simd_t>
299constexpr simd_t iota(typename simd_traits<simd_t>::scalar_type const offset)
300{
301 constexpr size_t length = simd_traits<simd_t>::length;
302 using scalar_type = typename simd_traits<simd_t>::scalar_type;
303 return detail::iota_impl<simd_t>(offset, std::make_integer_sequence<scalar_type, length>{});
304}
305
315template <simd::simd_concept simd_t>
316constexpr simd_t load(void const * mem_addr)
317{
318 assert(mem_addr != nullptr);
319 simd_t tmp{};
320
321 for (size_t i = 0; i < simd_traits<simd_t>::length; ++i)
322 tmp[i] = *(static_cast<typename simd_traits<simd_t>::scalar_type const *>(mem_addr) + i);
323
324 return tmp;
325}
326
328template <simd::simd_concept simd_t>
329 requires detail::is_builtin_simd_v<simd_t> &&
330 detail::is_native_builtin_simd_v<simd_t>
331constexpr simd_t load(void const * mem_addr)
332{
333 assert(mem_addr != nullptr);
334
335 if constexpr (simd_traits<simd_t>::max_length == 16)
336 return detail::load_sse4<simd_t>(mem_addr);
337 else if constexpr (simd_traits<simd_t>::max_length == 32)
338 return detail::load_avx2<simd_t>(mem_addr);
339 else if constexpr (simd_traits<simd_t>::max_length == 64)
340 return detail::load_avx512<simd_t>(mem_addr);
341 else
342 static_assert(simd_traits<simd_t>::max_length >= 16 && simd_traits<simd_t>::max_length <= 64,
343 "Unsupported simd type.");
344}
346
357template <simd::simd_concept simd_t>
358constexpr void store(void * mem_addr, simd_t const & simd_vec)
359{
360 assert(mem_addr != nullptr);
361 using scalar_t = typename simd_traits<simd_t>::scalar_type;
362
363 for (size_t i = 0; i < simd_traits<simd_t>::length; ++i)
364 *(static_cast<scalar_t *>(mem_addr) + i) = simd_vec[i];
365}
366
368template <simd::simd_concept simd_t>
369 requires detail::is_builtin_simd_v<simd_t> &&
370 detail::is_native_builtin_simd_v<simd_t>
371constexpr void store(void * mem_addr, simd_t const & simd_vec)
372{
373 assert(mem_addr != nullptr);
374
375 if constexpr (simd_traits<simd_t>::max_length == 16)
376 detail::store_sse4<simd_t>(mem_addr, simd_vec);
377 else if constexpr (simd_traits<simd_t>::max_length == 32)
378 detail::store_avx2<simd_t>(mem_addr, simd_vec);
379 else if constexpr (simd_traits<simd_t>::max_length == 64)
380 detail::store_avx512<simd_t>(mem_addr, simd_vec);
381 else
382 static_assert(simd_traits<simd_t>::max_length >= 16 && simd_traits<simd_t>::max_length <= 64,
383 "Unsupported simd type.");
384}
386
404template <simd::simd_concept simd_t>
405constexpr void transpose(std::array<simd_t, simd_traits<simd_t>::length> & matrix)
406{
408
409 for (size_t i = 0; i < matrix.size(); ++i)
410 for (size_t j = 0; j < matrix.size(); ++j)
411 tmp[j][i] = matrix[i][j];
412
413 std::swap(tmp, matrix);
414}
415
417// Implementation for seqan builtin simd.
418template <simd::simd_concept simd_t>
419 requires detail::is_builtin_simd_v<simd_t> &&
420 detail::is_native_builtin_simd_v<simd_t> &&
421 (simd_traits<simd_t>::max_length == simd_traits<simd_t>::length)
422constexpr void transpose(std::array<simd_t, simd_traits<simd_t>::length> & matrix)
423{
424 if constexpr (simd_traits<simd_t>::length == 16) // SSE4 implementation
425 detail::transpose_matrix_sse4(matrix);
426 else if constexpr (simd_traits<simd_t>::length == 32) // AVX2 implementation
427 detail::transpose_matrix_avx2(matrix);
428 else
429 transpose(matrix);
430}
432
441template <simd::simd_concept target_simd_t, simd::simd_concept source_simd_t>
442constexpr target_simd_t upcast(source_simd_t const & src)
443{
444 static_assert(simd_traits<target_simd_t>::length <= simd_traits<source_simd_t>::length,
445 "The length of the target simd type must be greater or equal than the length of the source simd type.");
446
447 target_simd_t tmp{};
448 for (unsigned i = 0; i < simd_traits<target_simd_t>::length; ++i)
449 tmp[i] = static_cast<typename simd_traits<target_simd_t>::scalar_type>(src[i]);
450
451 return tmp;
452}
453
455template <simd::simd_concept target_simd_t, simd::simd_concept source_simd_t>
456 requires detail::is_builtin_simd_v<target_simd_t> &&
457 detail::is_builtin_simd_v<source_simd_t> &&
458 detail::is_native_builtin_simd_v<source_simd_t>
459constexpr target_simd_t upcast(source_simd_t const & src)
460{
461 static_assert(simd_traits<target_simd_t>::length <= simd_traits<source_simd_t>::length,
462 "The length of the target simd type must be greater or equal than the length of the source simd type.");
463
464 if constexpr (simd_traits<source_simd_t>::length == simd_traits<target_simd_t>::length)
465 {
466 static_assert(simd_traits<target_simd_t>::max_length == simd_traits<source_simd_t>::max_length,
467 "Target vector has a different byte size.");
468 return reinterpret_cast<target_simd_t>(src); // Same packing so we do not cast.
469 }
470 else if constexpr (std::signed_integral<typename simd_traits<source_simd_t>::scalar_type>)
471 {
472 return detail::upcast_signed<target_simd_t>(src);
473 }
474 else
475 {
476 static_assert(std::unsigned_integral<typename simd_traits<source_simd_t>::scalar_type>,
477 "Expected unsigned scalar type.");
478 return detail::upcast_unsigned<target_simd_t>(src);
479 }
480}
482
483} // inline namespace simd
484
485} // namespace seqan3
Provides seqan3::detail::builtin_simd, seqan3::detail::is_builtin_simd and seqan3::simd::simd_traits<...
The <concepts> header from C++20's standard library.
T fill(T... args)
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
constexpr auto chunk
A chunk view.
Definition: chunk.hpp:29
T iota(T... args)
The main SeqAn3 namespace.
Definition: cigar_operation_table.hpp:2
Provides specific algorithm implementations for AVX2 instruction set.
Provides specific algorithm implementations for AVX512 instruction set.
Provides specific algorithm implementations for SSE4 instruction set.
Provides seqan3::simd::simd_traits.
T swap(T... args)
Provides seqan3::simd::simd_concept.