From d23848905bae98c94c574e1b5c01f109d4ebdc84 Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 21:47:33 +0100 Subject: [PATCH 1/9] [FEATURE] Use common traits class to expose static information contained in alignment configuration. --- .../alignment/pairwise/detail/type_traits.hpp | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/include/seqan3/alignment/pairwise/detail/type_traits.hpp b/include/seqan3/alignment/pairwise/detail/type_traits.hpp index b73ec1cb5e..b3ab14c3de 100644 --- a/include/seqan3/alignment/pairwise/detail/type_traits.hpp +++ b/include/seqan3/alignment/pairwise/detail/type_traits.hpp @@ -12,7 +12,13 @@ #pragma once +#include +#include #include +#include +#include +#include +#include #include #include #include @@ -42,4 +48,54 @@ struct chunked_indexed_sequence_pairs using type = decltype(views::zip(std::declval(), std::views::iota(0)) | views::chunk(1)); }; +/*!\brief A traits type for the alignment algorithm that exposes static information stored within the alignment + * configuration object. + * \ingroup pairwise_alignment + * + * \tparam config_t The type of the alignment configuration object; must be a specialisation of seqan3::configuration. + */ +template +//!\cond + requires is_type_specialisation_of_v +//!\endcond +struct alignment_configuration_traits +{ + //!\brief Flag to indicate vectorised mode. + static constexpr bool is_vectorised = config_t::template exists>(); + //!\brief Flag indicating whether parallel alignment mode is enabled. + static constexpr bool is_parallel = config_t::template exists(); + //!\brief Flag indicating whether local alignment mode is enabled. + static constexpr bool is_local = config_t::template exists>(); + //!\brief Flag indicating whether banded alignment mode is enabled. + static constexpr bool is_banded = config_t::template exists(); + //!\brief Flag indicating whether debug mode is enabled. + static constexpr bool is_debug = config_t::template exists(); + + //!\brief The configured alignment mode. + using alignment_mode_t = decltype(get(std::declval()).value); + //!\brief The selected scoring scheme. + using scoring_scheme_t = decltype(get(std::declval()).value); + //!\brief The alphabet of the selected scoring scheme. + using scoring_scheme_alphabet_t = typename scoring_scheme_t::alphabet_type; + //!\brief The selected result type. + using result_t = std::remove_reference_t(std::declval()))>; + //!\brief The original score type selected by the user. + using original_score_t = typename result_t::score_type; + //!\brief The score type for the alignment algorithm. + using score_t = std::conditional_t, original_score_t>; + //!\brief The trace directions type for the alignment algorithm. + using trace_t = std::conditional_t, trace_directions>; + + //!\brief The number of alignments that can be computed in one simd vector. + static constexpr size_t alignments_per_vector = [] () constexpr + { + if constexpr (is_vectorised) + return simd_traits::length; + else + return 1; + }(); + //!\brief The rank of the selected result type. + static constexpr int8_t result_type_rank = static_cast(decltype(std::declval().value)::rank); +}; + } // namespace seqan3::detail From a15e7845e514f8d0831b5cdfaa7c904830c8d6a0 Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 21:49:32 +0100 Subject: [PATCH 2/9] [FEATURE] Adds scoring scheme policy for the alignment algorithm. --- .../pairwise/policy/scoring_scheme_policy.hpp | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 include/seqan3/alignment/pairwise/policy/scoring_scheme_policy.hpp diff --git a/include/seqan3/alignment/pairwise/policy/scoring_scheme_policy.hpp b/include/seqan3/alignment/pairwise/policy/scoring_scheme_policy.hpp new file mode 100644 index 0000000000..0838910b97 --- /dev/null +++ b/include/seqan3/alignment/pairwise/policy/scoring_scheme_policy.hpp @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +/*!\file + * \brief Provides seqan3::detail::scoring_scheme_policy. + * \author Rene Rahn + */ + +#pragma once + +#include + +namespace seqan3::detail +{ + +/*!\brief The CRTP-policy that stores the scoring scheme used for this alignment algorithm. + * \ingroup alignment_policy + * \tparam alignment_algorithm_t The derived type (seqan3::detail::alignment_algorithm) to be augmented with this + * CRTP-policy. + * \tparam scoring_scheme_t The type of the scoring scheme. + * + * \remarks The template parameters of this CRTP-policy are selected in the + * seqan3::detail::alignment_configurator::configure_scoring_scheme when selecting the alignment for the given + * configuration. + */ +template +class scoring_scheme_policy +{ +private: + //!\brief Befriends the derived class to grant it access to the private members. + friend alignment_algorithm_t; + + /*!\name Constructors, destructor and assignment + * \{ + */ + //!\brief Defaulted. + constexpr scoring_scheme_policy() noexcept = default; + //!\brief Defaulted. + constexpr scoring_scheme_policy(scoring_scheme_policy const &) noexcept = default; + //!\brief Defaulted. + constexpr scoring_scheme_policy(scoring_scheme_policy &&) noexcept = default; + //!\brief Defaulted. + constexpr scoring_scheme_policy & operator=(scoring_scheme_policy const &) noexcept = default; + //!\brief Defaulted. + constexpr scoring_scheme_policy & operator=(scoring_scheme_policy &&) noexcept = default; + //!\brief Defaulted. + ~scoring_scheme_policy() noexcept = default; + //!\} + + //!\brief The scoring scheme used for this alignment algorithm. + scoring_scheme_t scoring_scheme{}; +}; + +} // namespace seqan3::detail From b77906b0c81377ea64fbdf6f4bcabbea413bf12b Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 21:50:15 +0100 Subject: [PATCH 3/9] [FEATURE] Adds a gap policy for the vectorised alignment. --- .../seqan3/alignment/pairwise/policy/all.hpp | 2 + .../pairwise/policy/find_optimum_policy.hpp | 3 + .../policy/simd_affine_gap_policy.hpp | 182 ++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 include/seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp diff --git a/include/seqan3/alignment/pairwise/policy/all.hpp b/include/seqan3/alignment/pairwise/policy/all.hpp index dc1c3e8659..35d3ccd3ec 100644 --- a/include/seqan3/alignment/pairwise/policy/all.hpp +++ b/include/seqan3/alignment/pairwise/policy/all.hpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include //!\cond DEV /*!\defgroup alignment_policy Alignment policies diff --git a/include/seqan3/alignment/pairwise/policy/find_optimum_policy.hpp b/include/seqan3/alignment/pairwise/policy/find_optimum_policy.hpp index e4594c1d7c..7a3c6f2b75 100644 --- a/include/seqan3/alignment/pairwise/policy/find_optimum_policy.hpp +++ b/include/seqan3/alignment/pairwise/policy/find_optimum_policy.hpp @@ -92,6 +92,9 @@ class find_optimum_policy template friend class affine_gap_policy; + template + friend class simd_affine_gap_policy; + //!\brief Allow seqan3::detail::affine_gap_init_policy to access check_score. template friend class affine_gap_init_policy; diff --git a/include/seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp b/include/seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp new file mode 100644 index 0000000000..2e8df85131 --- /dev/null +++ b/include/seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp @@ -0,0 +1,182 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +/*!\file + * \brief Provides seqan3::detail::simd_affine_gap_policy. + * \author Rene Rahn + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace seqan3::detail +{ + +// ---------------------------------------------------------------------------- +// simd_affine_gap_policy +// ---------------------------------------------------------------------------- + +/*!\brief The CRTP-policy that computes a batch of cells in the alignment matrix using simd instructions. + * \ingroup alignment_policy + * \tparam alignment_algorithm_t The derived type (seqan3::detail::alignment_algorithm) to be augmented with this + * CRTP-policy. + * \tparam score_t The score type of the dynamic programming matrix; must model seqan3::simd::simd_concept. + * \tparam align_local_t A std::bool_constant to switch between local and global alignment. + * + * \details + * + * This CRTP-policy implements the recursion for the alignment algorithm with affine gaps using an inter-sequence + * vectorisation scheme. See `Rahn, R, et al. Generic accelerated sequence alignment in SeqAn using vectorization + * and multi-threading. Bioinformatics 34.20 (2018): 3437-3445.` for more information. + * + * \remarks The template parameters of this CRTP-policy are selected in the + * seqan3::detail::alignment_configurator::select_gap_policy when selecting the alignment for the given + * configuration. + */ +template +class simd_affine_gap_policy +{ +private: + //!\brief Befriends the derived class to grant it access to the private members. + friend alignment_algorithm_t; + + //!\brief The state of the alignment algorithm for affine gaps. + using alignment_state_t = alignment_algorithm_state; + + /*!\name Constructors, destructor and assignment + * \{ + */ + constexpr simd_affine_gap_policy() noexcept = default; //!< Defaulted + constexpr simd_affine_gap_policy(simd_affine_gap_policy const &) noexcept = default; //!< Defaulted + constexpr simd_affine_gap_policy(simd_affine_gap_policy &&) noexcept = default; //!< Defaulted + constexpr simd_affine_gap_policy & operator=(simd_affine_gap_policy const &) noexcept = default; //!< Defaulted + constexpr simd_affine_gap_policy & operator=(simd_affine_gap_policy &&) noexcept = default; //!< Defaulted + ~simd_affine_gap_policy() noexcept = default; //!< Defaulted + //!\} + + /*!\brief Computes the score of the current simd cell. + * \tparam cell_t The type of the current cell [for detailed information on the type see below]. + * \param[in,out] current_cell The current cell in the dynamic programming matrix. + * \param[in,out] state The state storing hot helper variables. + * \param[in] score The score of comparing the respective letters of the first and the second sequence. + * + * \details + * + * `cell_t` is the result type of dereferencing the zipped iterator over the respective alignment score matrix and + * the alignment trace matrix used inside of the seqan3::detail::alignment_matrix_policy. The first parameter + * stored in the zipped tuple is the seqan3::detail::alignment_score_matrix_proxy and the second value is the + * seqan3::detail::alignment_trace_matrix_proxy. + * + * In order to compute the maximum for two simd vectors gcc implements the ternary operator such that + * `std::max(a, b)` can be implemented as `(a > b) ? a : b`, where `(a > b)` returns a mask vector. This implements + * the compare-and-blend approach for simd vector types. + */ + template + constexpr void compute_cell(cell_t && current_cell, + alignment_algorithm_state & state, + score_t const score) const noexcept + { + // score_cell = seqan3::detail::alignment_score_matrix_proxy + // trace_cell = seqan3::detail::alignment_trace_matrix_proxy + auto & [score_cell, trace_cell] = current_cell; + constexpr bool with_trace = !decays_to_ignore_v>; + // Precompute the diagonal score. + score_t tmp = score_cell.diagonal + score; + + if constexpr (with_trace) + { + auto mask = tmp < score_cell.up; + tmp = (mask) ? score_cell.up : tmp; + trace_cell.current = (mask) ? trace_cell.up : convert_to_simd(trace_directions::diagonal) | trace_cell.up; + + mask = tmp < score_cell.r_left; + tmp = (mask) ? score_cell.r_left : tmp; + trace_cell.current = (mask) ? trace_cell.r_left : trace_cell.current | trace_cell.r_left; + } + else + { + tmp = (tmp < score_cell.up) ? score_cell.up : tmp; + tmp = (tmp < score_cell.r_left) ? score_cell.r_left : tmp; + } + + if constexpr (align_local_t::value) + { + tmp = (tmp < simd::fill(0)) + /*then*/ ? (trace_cell.current = convert_to_simd(trace_directions::none), simd::fill(0)) + /*else*/ : tmp; + } + + // Store the current max score. + score_cell.current = tmp; + // Check if this was the optimum. Possibly a noop. + static_cast(*this).check_score_of_cell(current_cell, state); + + // Prepare horizontal and vertical score for next column. + tmp += state.gap_open_score; + score_cell.up += state.gap_extension_score; + score_cell.w_left = score_cell.r_left + state.gap_extension_score; + + auto mask = score_cell.up < tmp; + score_cell.up = (mask) ? tmp : score_cell.up; + trace_cell.up = (mask) ? convert_to_simd(trace_directions::up_open) : convert_to_simd(trace_directions::up); + mask = score_cell.w_left < tmp; + score_cell.w_left = (mask) ? tmp : score_cell.w_left; + trace_cell.w_left = (mask) ? convert_to_simd(trace_directions::left_open) + : convert_to_simd(trace_directions::left); + } + + /*!\brief Initialise the alignment state for affine gap computation. + * \tparam alignment_configuration_t The type of alignment configuration. + * \param[in] config The alignment configuration. + * \returns The initialised seqan3::detail::alignment_algorithm_state. + * + * \details + * + * Gets the stored gap scheme from the configuration or uses a default gap scheme if not set and initialises the + * alignment algorithm state with the respective gap extension and gap open costs. If the gap scheme was not + * specified by the user the following defaults are used: + * * `-1` for the gap extension score, and + * * `-10` for the gap open score. + */ + template + constexpr void initialise_alignment_state(alignment_configuration_t const & config) noexcept + { + using scalar_t = typename simd_traits::scalar_type; + auto scheme = config.template value_or(gap_scheme{gap_score{-1}, gap_open_score{-10}}); + + alignment_state.gap_extension_score = simd::fill(static_cast(scheme.get_gap_score())); + alignment_state.gap_open_score = simd::fill(static_cast(scheme.get_gap_score() + + scheme.get_gap_open_score())); + } + + /*!\brief Converts a trace direction into a simd vector. + * \param[in] direction The trace direction to convert to a simd vector. + */ + constexpr score_t convert_to_simd(trace_directions const direction) const noexcept + { + using scalar_t = typename simd_traits::scalar_type; + + return simd::fill(static_cast(direction)); + } + + alignment_state_t alignment_state{}; //!< The internal alignment state tracking the current alignment optimum. +}; + +} // namespace seqan3::detail From 321567ddc1e4339d364e85ef9d8c20e858eea934 Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 21:51:38 +0100 Subject: [PATCH 4/9] [FEATURE] Let the scoring scheme expose their alphabet type. --- include/seqan3/alignment/scoring/scoring_scheme_base.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/seqan3/alignment/scoring/scoring_scheme_base.hpp b/include/seqan3/alignment/scoring/scoring_scheme_base.hpp index 5132f8f582..4126deef53 100644 --- a/include/seqan3/alignment/scoring/scoring_scheme_base.hpp +++ b/include/seqan3/alignment/scoring/scoring_scheme_base.hpp @@ -105,6 +105,8 @@ class scoring_scheme_base */ //!\brief Type of the score values. using score_type = score_t; + //!\brief Type of the underlying alphabet. + using alphabet_type = alphabet_t; //!\brief Size type that can hold the dimension of the matrix (i.e. size of the alphabet). using matrix_size_type = std::remove_const_t)>; //!\} From 0c39d2f286f92a11508b7aea99d0dbd368e0b1cc Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 21:52:50 +0100 Subject: [PATCH 5/9] [FEATURE] Make affine gap inti policy work with simd vector score types. --- .../policy/affine_gap_init_policy.hpp | 57 ++++++++++++++----- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/include/seqan3/alignment/pairwise/policy/affine_gap_init_policy.hpp b/include/seqan3/alignment/pairwise/policy/affine_gap_init_policy.hpp index f5652007f5..732646b584 100644 --- a/include/seqan3/alignment/pairwise/policy/affine_gap_init_policy.hpp +++ b/include/seqan3/alignment/pairwise/policy/affine_gap_init_policy.hpp @@ -88,33 +88,33 @@ class affine_gap_init_policy auto & [score_cell, trace_cell] = origin_cell; // Initialise the first cell. - score_cell.current = 0; - trace_cell.current = trace_directions::none; + score_cell.current = convert_to_simd_maybe(0); + trace_cell.current = convert_to_simd_maybe(trace_directions::none); static_cast(*this).check_score_of_cell(origin_cell, state); // Initialise the vertical matrix cell according to the traits settings. if constexpr (traits_type::free_second_leading_t::value) { - score_cell.up = 0; - trace_cell.up = trace_directions::none; + score_cell.up = convert_to_simd_maybe(0); + trace_cell.up = convert_to_simd_maybe(trace_directions::none); } else // Initialise with gap_open score { score_cell.up = state.gap_open_score; - trace_cell.up = trace_directions::up_open; + trace_cell.up = convert_to_simd_maybe(trace_directions::up_open); } // Initialise the horizontal matrix cell according to the traits settings. if constexpr (traits_type::free_first_leading_t::value) { - score_cell.w_left = 0; - trace_cell.w_left = trace_directions::none; + score_cell.w_left = convert_to_simd_maybe(0); + trace_cell.w_left = convert_to_simd_maybe(trace_directions::none); } else // Initialise with gap_open score { score_cell.w_left = state.gap_open_score; - trace_cell.w_left = trace_directions::left_open; + trace_cell.w_left = convert_to_simd_maybe(trace_directions::left_open); } } @@ -147,16 +147,16 @@ class affine_gap_init_policy // Initialise the vertical matrix cell according to the traits settings. if constexpr (traits_type::free_second_leading_t::value) { - score_cell.up = 0; + score_cell.up = convert_to_simd_maybe(0); } else { score_cell.up += state.gap_extension_score; - trace_cell.up = trace_directions::up; + trace_cell.up = convert_to_simd_maybe(trace_directions::up); } score_cell.w_left = score_cell.current + state.gap_open_score; - trace_cell.w_left = trace_directions::left_open; + trace_cell.w_left = convert_to_simd_maybe(trace_directions::left_open); } /*!\brief Initialises a cell in the first row of the dynamic programming matrix. @@ -186,19 +186,46 @@ class affine_gap_init_policy static_cast(*this).check_score_of_cell(row_cell, state); score_cell.up = score_cell.current + state.gap_open_score; - trace_cell.up = trace_directions::up_open; + trace_cell.up = convert_to_simd_maybe(trace_directions::up_open); if constexpr (traits_type::free_first_leading_t::value) { - score_cell.w_left = 0; - trace_cell.w_left = trace_directions::none; + score_cell.w_left = convert_to_simd_maybe(0); + trace_cell.w_left = convert_to_simd_maybe(trace_directions::none); } else { score_cell.w_left = score_cell.r_left + state.gap_extension_score; - trace_cell.w_left = trace_directions::left; + trace_cell.w_left = convert_to_simd_maybe(trace_directions::left); } } + +private: + /*!\brief Converts the given value into a simd vector or just returns the value if alignment is not + * executed in vectorised mode. + * \tparam score_t The type of the score; must model either seqan3::simd::simd_concept or + * seqan3::arithmetic. + * \tparam value_t The value type to convert; must model seqan3::arithmetic. + * + * \param[in] value The value to possibly convert. + * + * \returns A simd vector filled with the given value or the value itself if the alignment is not executed + * in vectorised mode. + */ + template + constexpr auto convert_to_simd_maybe(value_t const value) const noexcept + { + if constexpr (simd_concept) + { + using scalar_t = typename simd_traits::scalar_type; + return simd::fill(static_cast(value)); + } + else + { + return value; + } + } + }; } // namespace seqan3::detail From fc3f4db6a4bff22ab5c08919584c592b8ab1276e Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 23:31:53 +0100 Subject: [PATCH 6/9] [TEST] Disable test for vectorised alignment. Not all tests can work at the moment with the vectorised alignment code and must be disabled for now. For some test cases these can be reenabled when the vectorisation is implemented. --- .../pairwise/align_pairwise_test.cpp | 4 +- ...global_affine_unbanded_collection_test.cpp | 6 +- ...ise_alignment_collection_test_template.hpp | 80 +++++++++++-------- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/test/unit/alignment/pairwise/align_pairwise_test.cpp b/test/unit/alignment/pairwise/align_pairwise_test.cpp index 2de20c0690..6fa3b4d768 100644 --- a/test/unit/alignment/pairwise/align_pairwise_test.cpp +++ b/test/unit/alignment/pairwise/align_pairwise_test.cpp @@ -38,9 +38,7 @@ struct align_pairwise_test : ::testing::Test }; using testing_types = ::testing::Types>; + align_cfg::parallel>; TYPED_TEST_CASE(align_pairwise_test, testing_types); diff --git a/test/unit/alignment/pairwise/global_affine_unbanded_collection_test.cpp b/test/unit/alignment/pairwise/global_affine_unbanded_collection_test.cpp index 5f08b3a06a..a27bc898aa 100644 --- a/test/unit/alignment/pairwise/global_affine_unbanded_collection_test.cpp +++ b/test/unit/alignment/pairwise/global_affine_unbanded_collection_test.cpp @@ -29,14 +29,14 @@ static auto dna4_01 = []() for (size_t i = 0; i < 100; ++i) data.push_back(fixture::global::affine::unbanded::dna4_01); - return alignment_fixture_collection{fixture::global::affine::unbanded::dna4_01.config, data}; + auto config = fixture::global::affine::unbanded::dna4_01.config | align_cfg::parallel{4}; + return alignment_fixture_collection{config, data}; }(); } // namespace seqan3::test::alignment::fixture::collection::global::affine::unbanded using pairwise_global_affine_collection_unbanded_testing_types = ::testing::Types< - pairwise_alignment_fixture<&collection::global::affine::unbanded::dna4_01, detail::vectorise_tag>, - pairwise_alignment_fixture<&collection::global::affine::unbanded::dna4_01, align_cfg::parallel> + pairwise_alignment_fixture<&collection::global::affine::unbanded::dna4_01> >; INSTANTIATE_TYPED_TEST_CASE_P(pairwise_global_affine_collection_unbanded, diff --git a/test/unit/alignment/pairwise/pairwise_alignment_collection_test_template.hpp b/test/unit/alignment/pairwise/pairwise_alignment_collection_test_template.hpp index 702276c411..e5bd1148c4 100644 --- a/test/unit/alignment/pairwise/pairwise_alignment_collection_test_template.hpp +++ b/test/unit/alignment/pairwise/pairwise_alignment_collection_test_template.hpp @@ -22,15 +22,13 @@ using namespace seqan3; using namespace seqan3::detail; using namespace seqan3::test::alignment::fixture; -template +template struct pairwise_alignment_fixture : public ::testing::Test { auto fixture() -> decltype(alignment_fixture_collection{*_fixture}) const & { return *_fixture; } - - using policy_t = exec_policy_t; }; template @@ -44,7 +42,7 @@ TYPED_TEST_P(pairwise_alignment_collection_test, score) auto const & fixture = this->fixture(); configuration align_cfg = fixture.config | align_cfg::result{with_score}; auto [database, query] = fixture.get_sequences(); - auto alignment_rng = align_pairwise(views::zip(database, query), align_cfg | typename TestFixture::policy_t{}); + auto alignment_rng = align_pairwise(views::zip(database, query), align_cfg); auto scores = fixture.get_scores(); EXPECT_TRUE((std::ranges::equal(alignment_rng | std::views::transform([] (auto res) { return res.score(); } ), @@ -56,7 +54,7 @@ TYPED_TEST_P(pairwise_alignment_collection_test, back_coordinate) auto const & fixture = this->fixture(); configuration align_cfg = fixture.config | align_cfg::result{with_back_coordinate}; auto [database, query] = fixture.get_sequences(); - auto res_vec = align_pairwise(views::zip(database, query), align_cfg | typename TestFixture::policy_t{}) + auto res_vec = align_pairwise(views::zip(database, query), align_cfg) | views::to; EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.score(); }), @@ -69,44 +67,56 @@ TYPED_TEST_P(pairwise_alignment_collection_test, front_coordinate) { auto const & fixture = this->fixture(); configuration align_cfg = fixture.config | align_cfg::result{with_front_coordinate}; - auto [database, query] = fixture.get_sequences(); - auto res_vec = align_pairwise(views::zip(database, query), align_cfg | typename TestFixture::policy_t{}) - | views::to; - EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.score(); }), - fixture.get_scores()))); - EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.back_coordinate(); }), - fixture.get_back_coordinates()))); - EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.front_coordinate(); }), - fixture.get_front_coordinates()))); + using traits_t = alignment_configuration_traits; + + if constexpr (!traits_t::is_vectorised) + { + auto [database, query] = fixture.get_sequences(); + auto res_vec = align_pairwise(views::zip(database, query), align_cfg) + | views::to; + + EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.score(); }), + fixture.get_scores()))); + EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.back_coordinate(); }), + fixture.get_back_coordinates()))); + EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.front_coordinate(); }), + fixture.get_front_coordinates()))); + } } TYPED_TEST_P(pairwise_alignment_collection_test, alignment) { auto const & fixture = this->fixture(); configuration align_cfg = fixture.config | align_cfg::result{with_alignment}; - auto [database, query] = fixture.get_sequences(); - auto res_vec = align_pairwise(views::zip(database, query), align_cfg | typename TestFixture::policy_t{}) - | views::to; - EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.score(); }), - fixture.get_scores()))); - EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.back_coordinate(); }), - fixture.get_back_coordinates()))); - EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.front_coordinate(); }), - fixture.get_front_coordinates()))); - EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) - { - return std::get<0>(res.alignment()) | views::to_char - | views::to; - }), - fixture.get_aligned_sequences1()))); - EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) - { - return std::get<1>(res.alignment()) | views::to_char - | views::to; - }), - fixture.get_aligned_sequences2()))); + using traits_t = alignment_configuration_traits; + + if constexpr (!traits_t::is_vectorised) + { + auto [database, query] = fixture.get_sequences(); + auto res_vec = align_pairwise(views::zip(database, query), align_cfg) + | views::to; + + EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.score(); }), + fixture.get_scores()))); + EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.back_coordinate(); }), + fixture.get_back_coordinates()))); + EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.front_coordinate(); }), + fixture.get_front_coordinates()))); + EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) + { + return std::get<0>(res.alignment()) | views::to_char + | views::to; + }), + fixture.get_aligned_sequences1()))); + EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) + { + return std::get<1>(res.alignment()) | views::to_char + | views::to; + }), + fixture.get_aligned_sequences2()))); + } } REGISTER_TYPED_TEST_CASE_P(pairwise_alignment_collection_test, From 6ad403410f6ea68d0a974796addbf5ba93fa7186 Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 23:32:34 +0100 Subject: [PATCH 7/9] [MISC] Use internal variable for initialisation of the chunk view. --- .../alignment/pairwise/execution/alignment_executor_two_way.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/seqan3/alignment/pairwise/execution/alignment_executor_two_way.hpp b/include/seqan3/alignment/pairwise/execution/alignment_executor_two_way.hpp index bceaed5b60..c7be3c589b 100644 --- a/include/seqan3/alignment/pairwise/execution/alignment_executor_two_way.hpp +++ b/include/seqan3/alignment/pairwise/execution/alignment_executor_two_way.hpp @@ -165,7 +165,7 @@ class alignment_executor_two_way if (chunk_size == 0u) throw std::invalid_argument{"The chunk size must be greater than 0."}; - chunked_resource = views::zip(std::forward(resrc), std::views::iota(0)) | views::chunk(chunk_size); + chunked_resource = views::zip(std::forward(resrc), std::views::iota(0)) | views::chunk(_chunk_size); chunked_resource_it = chunked_resource.begin(); if constexpr (std::same_as) From 8ff4827db7fd8cb36fcca327b4b39827dadc5b1c Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 23:35:16 +0100 Subject: [PATCH 8/9] [FEATURE] Enable vectorised alignment computation. --- .../alignment/pairwise/align_pairwise.hpp | 18 +- .../pairwise/align_result_selector.hpp | 11 +- .../pairwise/alignment_algorithm.hpp | 267 ++++++++++++------ .../pairwise/alignment_configurator.hpp | 225 ++++++++------- .../alignment/pairwise/detail/concept.hpp | 47 +++ 5 files changed, 368 insertions(+), 200 deletions(-) diff --git a/include/seqan3/alignment/pairwise/align_pairwise.hpp b/include/seqan3/alignment/pairwise/align_pairwise.hpp index 6142b1e238..f0fdd4cf22 100644 --- a/include/seqan3/alignment/pairwise/align_pairwise.hpp +++ b/include/seqan3/alignment/pairwise/align_pairwise.hpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -148,7 +150,7 @@ constexpr auto align_pairwise(sequence_t && seq, alignment_config_t const & conf //!\cond template - requires detail::align_pairwise_range_input_concept && + requires detail::align_pairwise_range_input && detail::is_type_specialisation_of_v constexpr auto align_pairwise(sequence_t && sequences, alignment_config_t const & config) @@ -166,26 +168,20 @@ constexpr auto align_pairwise(sequence_t && sequences, // Configure the alignment algorithm. auto && [algorithm, adapted_config] = detail::alignment_configurator::configure(config); + using traits_t = detail::alignment_configuration_traits>; //!brief Lambda function to translate specified parallel and or vectorised configurations into their execution rules. constexpr auto get_execution_rule = [] () { - if constexpr (alignment_config_t::template exists()) - return seqan3::par; + if constexpr (traits_t::is_parallel) + return seqan3::par; else return seqan3::seq; }; - size_t chunk_size = 1; - if constexpr (alignment_config_t::template exists()) - { - using score_t = typename detail::align_config_result_score::type; - chunk_size = simd_traits>::length; - } - // Create a two-way executor for the alignment. detail::alignment_executor_two_way executor{std::move(seq_view), std::move(algorithm), - chunk_size, + traits_t::alignments_per_vector, get_execution_rule()}; // Return the range over the alignments. return alignment_range{std::move(executor)}; diff --git a/include/seqan3/alignment/pairwise/align_result_selector.hpp b/include/seqan3/alignment/pairwise/align_result_selector.hpp index 99a34cdc5e..c493146b1b 100644 --- a/include/seqan3/alignment/pairwise/align_result_selector.hpp +++ b/include/seqan3/alignment/pairwise/align_result_selector.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -71,14 +72,14 @@ template ::original_score_t; + //!\brief Helper function to determine the actual result type. static constexpr auto select() { static_assert(configuration_t::template exists()); - using result_type = decltype(get(std::declval())); - using score_type = typename std::remove_reference_t::score_type; - if constexpr (configuration_t::template exists>()) { return alignment_result_value_type()) { using as_type_list = transfer_template_args_onto_t; - using score_matrix_t = two_dimensional_matrix, - std::allocator>, + using score_matrix_t = two_dimensional_matrix, + std::allocator>, matrix_major_order::column>; using trace_matrix_t = two_dimensional_matrix, std::allocator>, diff --git a/include/seqan3/alignment/pairwise/alignment_algorithm.hpp b/include/seqan3/alignment/pairwise/alignment_algorithm.hpp index 11851ce616..c6da2cc205 100644 --- a/include/seqan3/alignment/pairwise/alignment_algorithm.hpp +++ b/include/seqan3/alignment/pairwise/alignment_algorithm.hpp @@ -23,11 +23,18 @@ #include #include #include +#include #include #include +#include +#include +#include +#include #include +#include #include +#include #include #include #include @@ -73,19 +80,9 @@ class alignment_algorithm : public invoke_deferred_crtp_base>... { private: - //!\brief Check if the alignment is banded. - static constexpr bool is_banded = config_t::template exists(); - //!\brief Check if debug mode is enabled. - static constexpr bool is_debug_mode = config_t::template exists(); - - //!\brief The type of the stored scoring scheme. - using score_scheme_t = decltype(seqan3::get(std::declval()).value); - //!\brief The type of a column inside of the score matrix. - using score_matrix_column_t = - std::ranges::iter_value_t().score_matrix_iter)>; - //!\brief The type of a column inside of the trace matrix. - using trace_matrix_column_t = - std::ranges::iter_value_t().trace_matrix_iter)>; + + //!\brief The alignment configuration traits type with auxiliary information extracted from the configuration type. + using traits_t = alignment_configuration_traits; //!\brief The type of an alignment column as defined by the respective matrix policy. using alignment_column_t = decltype(std::declval().current_alignment_column()); //!\brief The iterator type over the alignment column. @@ -93,14 +90,14 @@ class alignment_algorithm : //!\brief The type of the score debug matrix. using score_debug_matrix_t = - std::conditional_t, - std::allocator>, + std::conditional_t, + std::allocator>, matrix_major_order::column>, empty_type>; //!\brief The type of the trace debug matrix. using trace_debug_matrix_t = - std::conditional_t, std::allocator>, matrix_major_order::column>, @@ -127,7 +124,7 @@ class alignment_algorithm : */ explicit constexpr alignment_algorithm(config_t const & cfg) : cfg_ptr{std::make_shared(cfg)} { - score_scheme = seqan3::get(*cfg_ptr).value; + this->scoring_scheme = seqan3::get(*cfg_ptr).value; this->initialise_alignment_state(*cfg_ptr); } //!\} @@ -177,6 +174,9 @@ class alignment_algorithm : * |space (alignment) |\f$ O(n*m) \f$ |\f$ O(n*k) \f$ | */ template + //!\cond + requires !traits_t::is_vectorised + //!\endcond auto operator()(indexed_sequence_pairs_t && indexed_sequence_pairs) { using sequence_pairs_t = std::tuple_element_t<0, std::ranges::range_value_t>; @@ -192,9 +192,66 @@ class alignment_algorithm : return results; } + + //!\overload + template + auto operator()(indexed_sequence_pairs_t && indexed_sequence_pairs) + //!\cond + requires traits_t::is_vectorised + //!\endcond + { + assert(cfg_ptr != nullptr); + + static_assert(simd_concept, "Expected simd score type."); + static_assert(simd_concept, "Expected simd trace type."); + + // Extract the batch of sequences for the first and the second sequence. + auto sequence1_range = indexed_sequence_pairs | views::get<0> | views::get<0>; + auto sequence2_range = indexed_sequence_pairs | views::get<0> | views::get<1>; + + // Convert batch of sequences to sequence of simd vectors. + auto simd_sequences1 = convert_batch_of_sequences_to_simd_vector(sequence1_range); + auto simd_sequences2 = convert_batch_of_sequences_to_simd_vector(sequence2_range); + + // Reset the alignment state's optimum between executions of the alignment algorithm. + this->alignment_state.reset_optimum(); + + compute_matrix(simd_sequences1, simd_sequences2); + + return make_alignment_result(indexed_sequence_pairs | views::get<1>, sequence1_range, sequence2_range); + } //!\} private: + /*!\brief Converts a batch of sequences to a sequence of simd vectors. + * \tparam sequence_range_t The type of the range over sequences; must model std::ranges::forward_range. + * + * \param[in] sequences The batch of sequences to transform. + * + * \returns a sequence over simd vectors. + * + * \details + * + * Expects that the size of the batch is less or equal than the number of alignments that can be computed within one + * simd vector. Applies an Array-of-Structures (AoS) to Structure-of-Arrays (SoA) transformation by storing one + * column of the batch as a simd vector. + */ + template + constexpr auto convert_batch_of_sequences_to_simd_vector(sequence_range_t & sequences) + { + assert(static_cast(std::ranges::distance(sequences)) <= traits_t::alignments_per_vector); + + using simd_score_t = typename traits_t::score_t; + + std::vector> simd_sequence{}; + + for (auto && simd_vector_chunk : sequences | views::to_simd) + for (auto && simd_vector : simd_vector_chunk) + simd_sequence.push_back(std::move(simd_vector)); + + return simd_sequence; + } + /*!\brief Computes the pairwise sequence alignment for a single pair of sequences. * \tparam sequence1_t The type of the first sequence; must model std::ranges::forward_range. * \tparam sequence2_t The type of the second sequence; must model std::ranges::forward_range. @@ -217,13 +274,27 @@ class alignment_algorithm : { assert(cfg_ptr != nullptr); - if constexpr (is_debug_mode) + if constexpr (traits_t::is_debug) initialise_debug_matrices(sequence1, sequence2); // Reset the alignment state's optimum between executions of the alignment algorithm. this->alignment_state.reset_optimum(); - return alignment_result{compute_matrix(idx, sequence1, sequence2)}; + if constexpr (traits_t::is_banded) + { + // Get the band and check if band configuration is valid. + auto const & band = seqan3::get(*cfg_ptr).value; + check_valid_band_parameter(sequence1, sequence2, band); + auto && [subsequence1, subsequence2] = this->slice_sequences(sequence1, sequence2, band); + // It would be great to use this interface here instead + compute_matrix(subsequence1, subsequence2, band); + return make_alignment_result(idx, subsequence1, subsequence2); + } + else + { + compute_matrix(sequence1, sequence2); + return make_alignment_result(idx, sequence1, sequence2); + } } /*!\brief Checks if the band parameters are valid for the given sequences. @@ -293,18 +364,16 @@ class alignment_algorithm : } /*!\brief Compute the alignment by iterating over the alignment matrix in a column wise manner. - * \tparam index_t The type of the index. * \tparam sequence1_t The type of the first sequence. * \tparam sequence2_t The type of the second sequence. * - * \param[in] idx The corresponding index for the given sequence pair. * \param[in] sequence1 The first sequence. * \param[in] sequence2 The second sequence. */ - template - auto compute_matrix(index_t const idx, sequence1_t & sequence1, sequence2_t & sequence2) + template + void compute_matrix(sequence1_t & sequence1, sequence2_t & sequence2) //!\cond - requires !is_banded + requires !traits_t::is_banded //!\endcond { // ---------------------------------------------------------------------------- @@ -328,42 +397,35 @@ class alignment_algorithm : // Wrap up phase: track score in last column and prepare the alignment result. // ---------------------------------------------------------------------------- - return finalise_alignment(idx, sequence1, sequence2); + finalise_alignment(); } //!\overload - template - auto compute_matrix(index_t const idx, sequence1_t & sequence1, sequence2_t & sequence2) + template + void compute_matrix(sequence1_t & sequence1, sequence2_t & sequence2, static_band const & band) //!\cond - requires is_banded + requires traits_t::is_banded //!\endcond { // ---------------------------------------------------------------------------- // Initialisation phase: allocate memory and initialise first column. // ---------------------------------------------------------------------------- - // Get the band and check if band configuration is valid. - auto const & band = seqan3::get(*cfg_ptr).value; - check_valid_band_parameter(sequence1, sequence2, band); - - // Slice sequences such that band starts in origin and ends in sink. - auto && [seq1_slice, seq2_slice] = this->slice_sequences(sequence1, sequence2, band); - // Allocate and initialise first column. - this->allocate_matrix(seq1_slice, seq2_slice, band, this->alignment_state); + this->allocate_matrix(sequence1, sequence2, band, this->alignment_state); size_t last_row_index = this->score_matrix.band_row_index; - initialise_first_alignment_column(seq2_slice | views::take(last_row_index)); + initialise_first_alignment_column(sequence2 | views::take(last_row_index)); // ---------------------------------------------------------------------------- // 1st recursion phase: iterate as long as the band intersects with the first row. // ---------------------------------------------------------------------------- - size_t seq2_slice_size = std::ranges::distance(seq2_slice); - for (auto const & seq1_value : seq1_slice | views::take(this->score_matrix.band_col_index)) + size_t sequence2_size = std::ranges::distance(sequence2); + for (auto const & seq1_value : sequence1 | views::take(this->score_matrix.band_col_index)) { - compute_alignment_column(seq1_value, seq2_slice | views::take(++last_row_index)); + compute_alignment_column(seq1_value, sequence2 | views::take(++last_row_index)); // Only if band reached last row of matrix the last cell might be tracked. - finalise_last_cell_in_column(last_row_index >= seq2_slice_size); + finalise_last_cell_in_column(last_row_index >= sequence2_size); } // ---------------------------------------------------------------------------- @@ -371,19 +433,19 @@ class alignment_algorithm : // ---------------------------------------------------------------------------- size_t first_row_index = 0; - for (auto const & seq1_value : seq1_slice | views::drop(this->score_matrix.band_col_index)) + for (auto const & seq1_value : sequence1 | views::drop(this->score_matrix.band_col_index)) { // In the second phase the band moves in every column one base down on the second sequence. - compute_alignment_column(seq1_value, seq2_slice | views::slice(first_row_index++, ++last_row_index)); + compute_alignment_column(seq1_value, sequence2 | views::slice(first_row_index++, ++last_row_index)); // Only if band reached last row of matrix the last cell might be tracked. - finalise_last_cell_in_column(last_row_index >= seq2_slice_size); + finalise_last_cell_in_column(last_row_index >= sequence2_size); } // ---------------------------------------------------------------------------- // Wrap up phase: track score in last column and prepare the alignment result. // ---------------------------------------------------------------------------- - return finalise_alignment(idx, seq1_slice, seq2_slice); + finalise_alignment(); } /*!\brief Initialises the first column of the alignment matrix. @@ -415,7 +477,7 @@ class alignment_algorithm : // Finalise the last cell of the initial column. bool at_last_row = true; - if constexpr (is_banded) // If the band reaches until the last row of the matrix. + if constexpr (traits_t::is_banded) // If the band reaches until the last row of the matrix. at_last_row = static_cast(this->score_matrix.band_row_index) == this->score_matrix.num_rows - 1; finalise_last_cell_in_column(at_last_row); @@ -453,12 +515,14 @@ class alignment_algorithm : { this->compute_first_band_cell(*alignment_column_it, this->alignment_state, - score_scheme.score(seq1_value, *seq2_it)); + this->scoring_scheme.score(seq1_value, *seq2_it)); ++seq2_it; } for (; seq2_it != std::ranges::end(sequence2); ++seq2_it) - this->compute_cell(*++alignment_column_it, this->alignment_state, score_scheme.score(seq1_value, *seq2_it)); + this->compute_cell(*++alignment_column_it, + this->alignment_state, + this->scoring_scheme.score(seq1_value, *seq2_it)); } /*!\brief Finalises the last cell of the current alignment column. @@ -476,10 +540,21 @@ class alignment_algorithm : if (at_last_row) this->check_score_of_last_row_cell(*alignment_column_it, this->alignment_state); - if constexpr (is_debug_mode) + if constexpr (traits_t::is_debug) dump_alignment_column(); } + //!\brief Checks the last cell, respectively column for the alignment optimum. + constexpr void finalise_alignment() noexcept + { + // ---------------------------------------------------------------------------- + // Check for the optimum in last cell/column. + // ---------------------------------------------------------------------------- + + this->check_score_of_cells_in_last_column(alignment_column, this->alignment_state); + this->check_score_of_last_cell(*alignment_column_it, this->alignment_state); + } + /*!\brief Creates a new alignment result from the current alignment optimum and for the given pair of sequences. * \tparam index_t The type of the index. * \tparam sequence1_t The type of the first sequence. @@ -489,13 +564,13 @@ class alignment_algorithm : * \param[in] sequence1 The first range to get the alignment for if requested. * \param[in] sequence2 The second range to get the alignment for if requested. * - * \returns A seqan3::alignment_result with the requested alignment outcomes. + * \returns A seqan3::alignment_result with the requested alignment outcomes. A std::vector over + * seqan3::alignment_result objects if the alignment is run in vectorisation mode. * * \details * - * At first the last column/cell of the alignment matrix is checked for a new alignment optimum. - * Then the alignment result is prepared. Depending on the selected configuration the following is extracted and/or - * computed: + * Fills the alignment result with the requested values. Depending on the selected configuration the following + * is extracted and/or computed: * * 1. The alignment score. * 2. The end positions of the aligned range for the first and second sequence. @@ -504,19 +579,19 @@ class alignment_algorithm : * * If the alignment is run in debug mode (see seqan3::align_cfg::debug) the debug score and optionally trace matrix * are stored in the alignment result as well. + * + * ### Vectorisation + * + * In vectorisation mode this interface takes as input argument ranges over the indices and the corresponding + * sequences. Then it extracts the alignment result for every element in the range from the vectorised scores and + * coordinates as well as the trace matrix if applicable. */ template - constexpr auto finalise_alignment(index_t const & idx, - sequence1_t && sequence1, - sequence2_t && sequence2) + requires !traits_t::is_vectorised + constexpr auto make_alignment_result(index_t const idx, + sequence1_t & sequence1, + sequence2_t & sequence2) { - // ---------------------------------------------------------------------------- - // Check for the optimum in last cell/column. - // ---------------------------------------------------------------------------- - - this->check_score_of_cells_in_last_column(alignment_column, this->alignment_state); - this->check_score_of_last_cell(*alignment_column_it, this->alignment_state); - // ---------------------------------------------------------------------------- // Build the alignment result // ---------------------------------------------------------------------------- @@ -524,28 +599,25 @@ class alignment_algorithm : static_assert(config_t::template exists(), "The configuration must contain an align_cfg::result element."); - using result_t = typename align_result_selector::type; - result_t res{}; + using result_value_t = typename align_result_selector::type; + result_value_t res{}; res.id = idx; - using result_config_t = std::remove_reference_t< - decltype(seqan3::get(std::declval()).value)>; - // Choose what needs to be computed. - if constexpr (result_config_t::rank >= 0) // compute score + if constexpr (traits_t::result_type_rank >= 0) // compute score res.score = this->alignment_state.optimum.score; - if constexpr (result_config_t::rank >= 1) // compute back coordinate + if constexpr (traits_t::result_type_rank >= 1) // compute back coordinate { res.back_coordinate = alignment_coordinate{column_index_type{this->alignment_state.optimum.column_index}, row_index_type{this->alignment_state.optimum.row_index}}; // At some point this needs to be refactored so that it is not necessary to adapt the coordinate. - if constexpr (is_banded) + if constexpr (traits_t::is_banded) res.back_coordinate.second += res.back_coordinate.first - this->trace_matrix.band_col_index; } - if constexpr (result_config_t::rank >= 2) // compute front coordinate + if constexpr (traits_t::result_type_rank >= 2) // compute front coordinate { // Get a aligned sequence builder for banded or un-banded case. aligned_sequence_builder builder{sequence1, sequence2}; @@ -555,21 +627,60 @@ class alignment_algorithm : res.front_coordinate.first = trace_res.first_sequence_slice_positions.first; res.front_coordinate.second = trace_res.second_sequence_slice_positions.first; - if constexpr (result_config_t::rank == 3) // compute alignment + if constexpr (traits_t::result_type_rank == 3) // compute alignment res.alignment = std::move(trace_res.alignment); } // Store the matrices in debug mode. - if constexpr (is_debug_mode) + if constexpr (traits_t::is_debug) { res.score_debug_matrix = std::move(score_debug_matrix); - if constexpr (result_config_t::rank == 3) // compute alignment + if constexpr (traits_t::result_type_rank == 3) // compute alignment res.trace_debug_matrix = std::move(trace_debug_matrix); } return res; } + //!\overload + template + //!\cond + requires traits_t::is_vectorised + //!\endcond + constexpr auto make_alignment_result(index_range_t && index_range, + sequence1_range_t && sequence1_range, + [[maybe_unused]] sequence2_range_t && sequence2_range) + { + using sequence1_t = std::ranges::range_value_t; + using sequence2_t = std::ranges::range_value_t; + + using result_value_t = typename align_result_selector::type; + + size_t number_of_computed_alignments = std::ranges::distance(sequence1_range); + auto index_it = std::ranges::begin(index_range); + std::vector> results{number_of_computed_alignments}; + // Note we are not using the index range as it might be an infinity range. + for (size_t i = 0; i < number_of_computed_alignments; ++i, ++index_it) + { + result_value_t res{}; + res.id = *index_it; + + // Choose what needs to be computed. + if constexpr (traits_t::result_type_rank >= 0) // compute score + res.score = this->alignment_state.optimum.score[i]; + + if constexpr (traits_t::result_type_rank >= 1) // compute back coordinate + { + res.back_coordinate.first = this->alignment_state.optimum.column_index[i]; + res.back_coordinate.second = this->alignment_state.optimum.row_index[i]; + } + + results[i] = std::move(res); + } + + return results; + } + /*!\brief Dumps the current alignment matrix in the debug score matrix and if requested debug trace matrix. * * \details @@ -585,7 +696,7 @@ class alignment_algorithm : auto column = this->current_alignment_column(); auto coord = get<1>(column.front()).coordinate; - if constexpr (is_banded) + if constexpr (traits_t::is_banded) coord.second += coord.first - this->score_matrix.band_col_index; matrix_offset offset{row_index_type{static_cast(coord.second)}, @@ -611,8 +722,6 @@ class alignment_algorithm : //!\brief The alignment configuration stored on the heap. std::shared_ptr cfg_ptr{}; - //!\brief The scoring scheme used for this alignment algorithm. - score_scheme_t score_scheme{}; //!\brief Stores the currently processed alignment column. alignment_column_t alignment_column{}; //!\brief Stores the state of the currently processed alignment column. diff --git a/include/seqan3/alignment/pairwise/alignment_configurator.hpp b/include/seqan3/alignment/pairwise/alignment_configurator.hpp index 1c8cdd84fc..70ab56f28f 100644 --- a/include/seqan3/alignment/pairwise/alignment_configurator.hpp +++ b/include/seqan3/alignment/pairwise/alignment_configurator.hpp @@ -27,10 +27,13 @@ #include #include #include +#include #include +#include #include +#include #include -#include +#include #include #include #include @@ -38,24 +41,6 @@ namespace seqan3::detail { -//!\brief A helper concept to test for correct input in seqan3::align_pairwise. -//!\ingroup pairwise_alignment -template -SEQAN3_CONCEPT align_pairwise_value = - tuple_like && - std::tuple_size_v == 2 && - std::ranges::forward_range> && - std::ranges::forward_range>; - -//!\brief A helper concept to test for correct single value input in seqan3::align_pairwise. -//!\ingroup pairwise_alignment -//!\see seqan3::detail::align_pairwise_value -//!\see seqan3::detail::align_pairwise_range_input_concept -template -SEQAN3_CONCEPT align_pairwise_single_input = - align_pairwise_value && - std::ranges::viewable_range> && - std::ranges::viewable_range>; /*!\brief A transformation trait to extract the score type used within the seqan3::align_cfg::result object. * \implements seqan3::transformation_trait @@ -79,27 +64,6 @@ struct align_config_result_score using type = typename result_config_t::score_type; }; -/*!\brief A helper concept to test for correct range input in seqan3::align_pairwise. - * \ingroup pairwise_alignment - * - * \details - * - * Only use input ranges whose value type models seqan3::detail::align_pairwise_value and - * whose reference type is an lvalue reference and the range itself models std::ranges::viewable_range or - * the reference type is a prvalue and it models seqan3::detail::align_pairwise_single_input. - * This covers all typical use cases: - * a) A lvalue range, whose reference type is a tuple like lvalue reference, - * b) A range, whose reference type is a tuple over viewable ranges. - * This covers also transforming and non-transforming views (e.g. views::zip, or views::take). - * Only a temporary non-view range piped with views::persist can't be handled securely. - */ -template -SEQAN3_CONCEPT align_pairwise_range_input_concept = - std::ranges::input_range> && - align_pairwise_value> && - ((std::ranges::viewable_range && std::is_lvalue_reference_v>) || - align_pairwise_single_input>>); - /*!\brief Provides several contracts to test when configuring the alignment algorithm. * \ingroup pairwise_alignment * \tparam range_type The type of the range containing sequences to be aligned. @@ -167,22 +131,25 @@ struct alignment_configurator private: /*!\brief Transformation trait that chooses the correct matrix policy. - * \tparam config_type The configuration for which to select the correct matrix policy. - * \tparam score_t The value type used for the score matrix. - * \tparam trace_t The value type used for the trace matrix. + * \tparam traits_t The alignment configuration traits type. */ - template + template struct select_matrix_policy { private: + //!\brief Indicates whether only the coordinate is required to compute the alignment. + static constexpr bool only_coordinates = traits_t::result_type_rank < with_front_coordinate_type::rank; + //!\brief The selected score matrix for either banded or unbanded alignments. - using score_matrix_t = std::conditional_t(), - alignment_score_matrix_one_column_banded, - alignment_score_matrix_one_column>; + using score_matrix_t = std::conditional_t, + alignment_score_matrix_one_column>; //!\brief The selected trace matrix for either banded or unbanded alignments. - using trace_matrix_t = std::conditional_t(), - alignment_trace_matrix_full_banded, - alignment_trace_matrix_full>; + using trace_matrix_t = std::conditional_t, + alignment_trace_matrix_full>; public: //!\brief The matrix policy based on the configurations given by `config_type`. @@ -190,25 +157,23 @@ struct alignment_configurator }; /*!\brief Transformation trait that chooses the correct gap policy. - * \tparam config_type The configuration for which to select the correct gap policy. - * \tparam trait_types A template parameter pack with additional traits to augment the selected policy. + * \tparam traits_t The alignment configuration traits type. */ - template + template struct select_gap_policy { - //!\brief The matrix policy based on the configurations given by `config_type`. - using type = deferred_crtp_base; - }; + private: + + //!\brief The score type for the alignment computation. + using score_t = typename traits_t::score_t; + //!\brief The is_local constant converted to a type. + using is_local_t = std::bool_constant; + public: - /*!\brief Transformation trait that chooses the correct gap initialisation policy. - * \tparam config_type The configuration for which to select the correct gap initialisation policy. - * \tparam trait_types A template parameter pack with additional traits to augment the selected policy. - */ - template - struct select_gap_init_policy - { //!\brief The matrix policy based on the configurations given by `config_type`. - using type = deferred_crtp_base; + using type = std::conditional_t, + deferred_crtp_base>; }; public: @@ -237,7 +202,7 @@ struct alignment_configurator * Note that even if they are not passed as const lvalue reference (which is not possible, since not all views are * const-iterable), they are not modified within the alignment algorithm. */ - template + template //!\cond requires is_type_specialisation_of_v //!\endcond @@ -332,7 +297,7 @@ struct alignment_configurator throw invalid_alignment_configuration{"The align_cfg::max_error configuration is only allowed for " "the specific edit distance computation."}; // Configure the alignment algorithm. - return std::pair{configure_free_ends_initialisation(cfg), cfg}; + return std::pair{configure_scoring_scheme(cfg), cfg}; } } @@ -345,11 +310,13 @@ struct alignment_configurator template static constexpr function_wrapper_t configure_edit_distance(config_t const & cfg) { + using traits_t = alignment_configuration_traits; + // ---------------------------------------------------------------------------- // Unsupported configurations // ---------------------------------------------------------------------------- - if constexpr (config_t::template exists()) + if constexpr (traits_t::is_banded) throw invalid_alignment_configuration{"Banded alignments are yet not supported."}; // ---------------------------------------------------------------------------- @@ -411,24 +378,33 @@ struct alignment_configurator /*!\brief Configures the dynamic programming matrix initialisation accoring to seqan3::align_cfg::aligned_ends * settings. + * * \tparam function_wrapper_t The invocable alignment function type-erased via std::function. - * \tparam config_t The alignment configuration type. - * \param[in] cfg The passed configuration object. + * \tparam policies_t A template parameter pack for the already configured policy types. + * \tparam config_t The alignment configuration type. + * + * \param[in] cfg The passed configuration object. + * + * \returns the configured alignment algorithm. * * \details * * The matrix initialisation depends on the settings for the leading gaps for the first and the second sequence * within the seqan3::align_cfg::aligned_ends configuration element. */ - template + template static constexpr function_wrapper_t configure_free_ends_initialisation(config_t const & cfg); /*!\brief Configures the search space for the alignment algorithm according to seqan3::align_cfg::aligned_ends * settings. + * * \tparam function_wrapper_t The invocable alignment function type-erased via std::function. - * \tparam policies_t A template parameter pack for the already configured policy types. - * \tparam config_t The alignment configuration type. - * \param[in] cfg The passed configuration object. + * \tparam policies_t A template parameter pack for the already configured policy types. + * \tparam config_t The alignment configuration type. + * + * \param[in] cfg The passed configuration object. + * + * \returns the configured alignment algorithm. * * \details * @@ -437,37 +413,75 @@ struct alignment_configurator */ template static constexpr function_wrapper_t configure_free_ends_optimum_search(config_t const & cfg); + + /*!\brief Configures the scoring scheme to use for the alignment computation. + * + * \tparam function_wrapper_t The invocable alignment function type-erased via std::function. + * \tparam config_t The alignment configuration type. + * + * \param[in] cfg The passed configuration object. + * + * \returns the configured alignment algorithm. + * + * \details + * + * The correct scoring scheme is selected based on the vectorisation mode. If no vectorisation is enabled, the + * scoring scheme is the one configured in seqan3::align_config::scoring. If vectorisation is enabled, then the + * appropriate scoring scheme for the vectorised alignment algorithm is selected. This involves checking whether the + * passed scoring scheme is a matrix or a simple scoring scheme, which has only mismatch and match costs. + */ + template + static constexpr function_wrapper_t configure_scoring_scheme(config_t const & cfg); + + /*!\brief Constructs the actual alignment algorithm wrapped in the passed std::function object. + * + * \tparam function_wrapper_t The invocable alignment function type-erased via std::function. + * \tparam policies_t A template parameter pack for the already configured policy types. + * \tparam config_t The alignment configuration type. + * + * \param[in] cfg The passed configuration object. + * + * \returns the configured alignment algorithm. + * + * \details + * + * Configures the matrix and the gap policy and constructs the algorithm with the configured policies. + */ + template + static constexpr function_wrapper_t make_algorithm(config_t const & cfg) + { + using traits_t = alignment_configuration_traits; + using matrix_policy_t = typename select_matrix_policy::type; + using gap_policy_t = typename select_gap_policy::type; + + return alignment_algorithm{cfg}; + } }; //!\cond +template +constexpr function_wrapper_t alignment_configurator::configure_scoring_scheme(config_t const & cfg) +{ + using traits_t = alignment_configuration_traits; + + using alignment_scoring_scheme_t = + lazy_conditional_t, + typename traits_t::scoring_scheme_t>; + + using scoring_scheme_policy_t = deferred_crtp_base; + return configure_free_ends_initialisation(cfg); +} + // This function returns a std::function object which can capture runtime dependent alignment algorithm types through // a fixed invocation interface which is already defined by the caller of this function. -template +template constexpr function_wrapper_t alignment_configurator::configure_free_ends_initialisation(config_t const & cfg) { - // ---------------------------------------------------------------------------- - // score type - // ---------------------------------------------------------------------------- - - using score_type = typename std::remove_reference_t(cfg))>::score_type; - - // ---------------------------------------------------------------------------- - // dynamic programming matrix - // ---------------------------------------------------------------------------- - - using dp_matrix_t = typename select_matrix_policy::type; - - // ---------------------------------------------------------------------------- - // affine gap kernel - // ---------------------------------------------------------------------------- - - using local_t = std::bool_constant>()>; - using affine_t = typename select_gap_policy::type; - - // ---------------------------------------------------------------------------- - // configure initialisation policy - // ---------------------------------------------------------------------------- - + using traits_t = alignment_configuration_traits; // Get the value for the sequence ends configuration. auto align_ends_cfg = cfg.template value_or(free_ends_none); using align_ends_cfg_t = decltype(align_ends_cfg); @@ -484,11 +498,11 @@ constexpr function_wrapper_t alignment_configurator::configure_free_ends_initial }; // Make initialisation policy a deferred CRTP base and delegate to configure the find optimum policy. - using init_t = typename select_gap_init_policy::type; - return configure_free_ends_optimum_search(cfg); + using gap_init_policy_t = deferred_crtp_base; + return configure_free_ends_optimum_search(cfg); }; - if constexpr (local_t::value) + if constexpr (traits_t::is_local) { return configure_leading_both(std::true_type{}, std::true_type{}); } @@ -538,28 +552,28 @@ constexpr function_wrapper_t alignment_configurator::configure_free_ends_initial template constexpr function_wrapper_t alignment_configurator::configure_free_ends_optimum_search(config_t const & cfg) { + using traits_t = alignment_configuration_traits; + // Get the value for the sequence ends configuration. auto align_ends_cfg = cfg.template value_or(free_ends_none); using align_ends_cfg_t = decltype(align_ends_cfg); - using local_t = std::bool_constant>()>; - // This lambda augments the find optimum policy of the alignment algorithm with the // respective aligned_ends configuration. auto configure_trailing_both = [&] (auto first_seq, auto second_seq) constexpr { struct policy_trait_type { - using find_in_every_cell_type [[maybe_unused]] = local_t; + using find_in_every_cell_type [[maybe_unused]] = std::bool_constant; using find_in_last_row_type [[maybe_unused]] = decltype(first_seq); using find_in_last_column_type [[maybe_unused]] = decltype(second_seq); }; using find_optimum_t = deferred_crtp_base; - return function_wrapper_t{alignment_algorithm{cfg}}; + return make_algorithm(cfg); }; - if constexpr (local_t::value) + if constexpr (traits_t::is_local) { return configure_trailing_both(std::true_type{}, std::true_type{}); } @@ -602,4 +616,5 @@ constexpr function_wrapper_t alignment_configurator::configure_free_ends_optimum } } //!\endcond + } // namespace seqan3::detail diff --git a/include/seqan3/alignment/pairwise/detail/concept.hpp b/include/seqan3/alignment/pairwise/detail/concept.hpp index c6901a65dc..1c724cb5e5 100644 --- a/include/seqan3/alignment/pairwise/detail/concept.hpp +++ b/include/seqan3/alignment/pairwise/detail/concept.hpp @@ -90,4 +90,51 @@ SEQAN3_CONCEPT indexed_sequence_pair_range = std::ranges::forward_range && requires std::copy_constructible>; }; //!\endcond + +/*!\interface seqan3::detail::align_pairwise_single_input <> + * \brief A helper concept to test for correct single value input in seqan3::align_pairwise. + * \ingroup pairwise_alignment + * + * \tparam t The type to check. + * + * \details + * + * The given type must model seqan3::detail::sequence_pair and both contained types must model + * std::ranges::viewable_range. + * + * \see seqan3::detail::align_pairwise_range_input + */ +//!\cond +template +SEQAN3_CONCEPT align_pairwise_single_input = + sequence_pair && + std::ranges::viewable_range> && + std::ranges::viewable_range>; +//!\endcond + +/*!\interface seqan3::detail::align_pairwise_range_input <> + * \brief A helper concept to test for correct range input in seqan3::align_pairwise. + * \ingroup pairwise_alignment + * + * \tparam t The type to check. + * + * \details + * + * Only use input ranges whose value type models seqan3::detail::align_pairwise_value and + * whose reference type is an lvalue reference and the range itself models std::ranges::viewable_range or + * the reference type is a prvalue and it models seqan3::detail::align_pairwise_single_input. + * This covers all typical use cases: + * a) An lvalue range, whose reference type is a tuple like lvalue reference, + * b) A range, whose reference type is a tuple over viewable ranges. + * This covers also transforming and non-transforming views (e.g. views::zip, or views::take). + * Only a temporary non-view range piped with views::persist can't be handled securely. + */ +//!\cond +template +SEQAN3_CONCEPT align_pairwise_range_input = + std::ranges::forward_range && + sequence_pair> && + ((std::ranges::viewable_range && std::is_lvalue_reference_v>) || + align_pairwise_single_input>>); +//!\endcond } // namespace seqan3::detail From 940d8243293aaa0f2d795f9cfc190c845beb3af9 Mon Sep 17 00:00:00 2001 From: rrahn Date: Sat, 23 Nov 2019 23:36:35 +0100 Subject: [PATCH 9/9] [TEST] Adds first simple test case for vectorised alignment algorithm. * Tests dna sequences of same length. * Only score and back coordinate can be computed. --- .../pairwise/alignment_algorithm.hpp | 4 +- .../pairwise/alignment_configurator.hpp | 2 +- .../alignment/pairwise/detail/type_traits.hpp | 2 +- .../policy/simd_affine_gap_policy.hpp | 16 +++---- test/unit/alignment/pairwise/CMakeLists.txt | 1 + ...l_affine_unbanded_collection_simd_test.cpp | 44 +++++++++++++++++++ ...ise_alignment_collection_test_template.hpp | 4 +- 7 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 test/unit/alignment/pairwise/global_affine_unbanded_collection_simd_test.cpp diff --git a/include/seqan3/alignment/pairwise/alignment_algorithm.hpp b/include/seqan3/alignment/pairwise/alignment_algorithm.hpp index c6da2cc205..1430af02d7 100644 --- a/include/seqan3/alignment/pairwise/alignment_algorithm.hpp +++ b/include/seqan3/alignment/pairwise/alignment_algorithm.hpp @@ -194,7 +194,7 @@ class alignment_algorithm : } //!\overload - template + template auto operator()(indexed_sequence_pairs_t && indexed_sequence_pairs) //!\cond requires traits_t::is_vectorised @@ -587,7 +587,9 @@ class alignment_algorithm : * coordinates as well as the trace matrix if applicable. */ template + //!\cond requires !traits_t::is_vectorised + //!\endcond constexpr auto make_alignment_result(index_t const idx, sequence1_t & sequence1, sequence2_t & sequence2) diff --git a/include/seqan3/alignment/pairwise/alignment_configurator.hpp b/include/seqan3/alignment/pairwise/alignment_configurator.hpp index 70ab56f28f..bd07cd2d6b 100644 --- a/include/seqan3/alignment/pairwise/alignment_configurator.hpp +++ b/include/seqan3/alignment/pairwise/alignment_configurator.hpp @@ -162,8 +162,8 @@ struct alignment_configurator template struct select_gap_policy { - private: + private: //!\brief The score type for the alignment computation. using score_t = typename traits_t::score_t; //!\brief The is_local constant converted to a type. diff --git a/include/seqan3/alignment/pairwise/detail/type_traits.hpp b/include/seqan3/alignment/pairwise/detail/type_traits.hpp index b3ab14c3de..460bf91e17 100644 --- a/include/seqan3/alignment/pairwise/detail/type_traits.hpp +++ b/include/seqan3/alignment/pairwise/detail/type_traits.hpp @@ -95,7 +95,7 @@ struct alignment_configuration_traits return 1; }(); //!\brief The rank of the selected result type. - static constexpr int8_t result_type_rank = static_cast(decltype(std::declval().value)::rank); + static constexpr int8_t result_type_rank = static_cast(decltype(std::declval().value)::rank); }; } // namespace seqan3::detail diff --git a/include/seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp b/include/seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp index 2e8df85131..18fc0291d5 100644 --- a/include/seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp +++ b/include/seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp @@ -57,18 +57,18 @@ class simd_affine_gap_policy //!\brief Befriends the derived class to grant it access to the private members. friend alignment_algorithm_t; - //!\brief The state of the alignment algorithm for affine gaps. + //!\brief The type of state of the alignment algorithm for affine gaps. using alignment_state_t = alignment_algorithm_state; /*!\name Constructors, destructor and assignment * \{ */ - constexpr simd_affine_gap_policy() noexcept = default; //!< Defaulted - constexpr simd_affine_gap_policy(simd_affine_gap_policy const &) noexcept = default; //!< Defaulted - constexpr simd_affine_gap_policy(simd_affine_gap_policy &&) noexcept = default; //!< Defaulted - constexpr simd_affine_gap_policy & operator=(simd_affine_gap_policy const &) noexcept = default; //!< Defaulted - constexpr simd_affine_gap_policy & operator=(simd_affine_gap_policy &&) noexcept = default; //!< Defaulted - ~simd_affine_gap_policy() noexcept = default; //!< Defaulted + constexpr simd_affine_gap_policy() noexcept = default; //!< Defaulted. + constexpr simd_affine_gap_policy(simd_affine_gap_policy const &) noexcept = default; //!< Defaulted. + constexpr simd_affine_gap_policy(simd_affine_gap_policy &&) noexcept = default; //!< Defaulted. + constexpr simd_affine_gap_policy & operator=(simd_affine_gap_policy const &) noexcept = default; //!< Defaulted. + constexpr simd_affine_gap_policy & operator=(simd_affine_gap_policy &&) noexcept = default; //!< Defaulted. + ~simd_affine_gap_policy() noexcept = default; //!< Defaulted. //!\} /*!\brief Computes the score of the current simd cell. @@ -84,7 +84,7 @@ class simd_affine_gap_policy * stored in the zipped tuple is the seqan3::detail::alignment_score_matrix_proxy and the second value is the * seqan3::detail::alignment_trace_matrix_proxy. * - * In order to compute the maximum for two simd vectors gcc implements the ternary operator such that + * In order to compute the maximum for two simd vectors, gcc implements the ternary operator such that * `std::max(a, b)` can be implemented as `(a > b) ? a : b`, where `(a > b)` returns a mask vector. This implements * the compare-and-blend approach for simd vector types. */ diff --git a/test/unit/alignment/pairwise/CMakeLists.txt b/test/unit/alignment/pairwise/CMakeLists.txt index 2ffe138672..92f2754323 100644 --- a/test/unit/alignment/pairwise/CMakeLists.txt +++ b/test/unit/alignment/pairwise/CMakeLists.txt @@ -4,6 +4,7 @@ seqan3_test(alignment_result_test.cpp) seqan3_test(align_result_selector_test.cpp) seqan3_test(alignment_configurator_test.cpp) seqan3_test(global_affine_banded_test.cpp) +seqan3_test(global_affine_unbanded_collection_simd_test.cpp) seqan3_test(global_affine_unbanded_collection_test.cpp) seqan3_test(global_affine_unbanded_test.cpp) seqan3_test(local_affine_banded_test.cpp) diff --git a/test/unit/alignment/pairwise/global_affine_unbanded_collection_simd_test.cpp b/test/unit/alignment/pairwise/global_affine_unbanded_collection_simd_test.cpp new file mode 100644 index 0000000000..1b706d1513 --- /dev/null +++ b/test/unit/alignment/pairwise/global_affine_unbanded_collection_simd_test.cpp @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +#include + +#include + +#include + +#include "fixture/global_affine_unbanded.hpp" +#include "pairwise_alignment_collection_test_template.hpp" + +using namespace seqan3; +using namespace seqan3::detail; +using namespace seqan3::test::alignment; + +namespace seqan3::test::alignment::collection::simd::global::affine::unbanded +{ + +static auto dna4_01 = []() +{ + using fixture_t = decltype(fixture::global::affine::unbanded::dna4_01); + + std::vector data; + for (size_t i = 0; i < 100; ++i) + data.push_back(fixture::global::affine::unbanded::dna4_01); + + auto config = fixture::global::affine::unbanded::dna4_01.config | align_cfg::vectorise; + return alignment_fixture_collection{config, data}; +}(); + +} // namespace seqan3::test::alignment::collection::simd::global::affine::unbanded + +using pairwise_collection_simd_global_affine_unbanded_testing_types = ::testing::Types< + pairwise_alignment_fixture<&collection::simd::global::affine::unbanded::dna4_01> + >; + +INSTANTIATE_TYPED_TEST_CASE_P(pairwise_collection_simd_global_affine_unbanded, + pairwise_alignment_collection_test, + pairwise_collection_simd_global_affine_unbanded_testing_types); diff --git a/test/unit/alignment/pairwise/pairwise_alignment_collection_test_template.hpp b/test/unit/alignment/pairwise/pairwise_alignment_collection_test_template.hpp index e5bd1148c4..3fca5d20bf 100644 --- a/test/unit/alignment/pairwise/pairwise_alignment_collection_test_template.hpp +++ b/test/unit/alignment/pairwise/pairwise_alignment_collection_test_template.hpp @@ -74,7 +74,7 @@ TYPED_TEST_P(pairwise_alignment_collection_test, front_coordinate) { auto [database, query] = fixture.get_sequences(); auto res_vec = align_pairwise(views::zip(database, query), align_cfg) - | views::to; + | views::to; EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.score(); }), fixture.get_scores()))); @@ -96,7 +96,7 @@ TYPED_TEST_P(pairwise_alignment_collection_test, alignment) { auto [database, query] = fixture.get_sequences(); auto res_vec = align_pairwise(views::zip(database, query), align_cfg) - | views::to; + | views::to; EXPECT_TRUE((std::ranges::equal(res_vec | std::views::transform([] (auto res) { return res.score(); }), fixture.get_scores())));