From c467ff9c0e65fdf9aa725088249c7642b02cfb71 Mon Sep 17 00:00:00 2001 From: rrahn Date: Fri, 20 Dec 2019 19:29:42 +0100 Subject: [PATCH] [FEATURE] Make search work with dynamic mode configuration. Enables the dynamic mode configuration within the search algorithm by transforming it into a static configuration. --- .../seqan3/search/algorithm/detail/search.hpp | 42 ++++++++++++++++--- .../unit/search/search_configuration_test.cpp | 28 ++++++++++++- test/unit/search/search_test.cpp | 36 ++++++++++++++++ 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/include/seqan3/search/algorithm/detail/search.hpp b/include/seqan3/search/algorithm/detail/search.hpp index fea1535a58b..c345ba458f1 100644 --- a/include/seqan3/search/algorithm/detail/search.hpp +++ b/include/seqan3/search/algorithm/detail/search.hpp @@ -12,7 +12,10 @@ #pragma once +#include + #include +#include #include #include #include @@ -79,7 +82,7 @@ inline auto search_single(index_t const & index, query_t & query, configuration_ }; // choose mode - if constexpr (cfg_t::template exists>()) + if constexpr (cfg_t::template exists>()) { detail::search_param max_error2{max_error}; max_error2.total = 0; @@ -89,7 +92,7 @@ inline auto search_single(index_t const & index, query_t & query, configuration_ max_error2.total++; } } - else if constexpr (cfg_t::template exists>()) + else if constexpr (cfg_t::template exists>()) { detail::search_param max_error2{max_error}; max_error2.total = 0; @@ -99,7 +102,7 @@ inline auto search_single(index_t const & index, query_t & query, configuration_ max_error2.total++; } } - else if constexpr (cfg_t::template exists>()) + else if constexpr (cfg_t::template exists>()) { detail::search_param max_error2{max_error}; max_error2.total = 0; @@ -111,7 +114,7 @@ inline auto search_single(index_t const & index, query_t & query, configuration_ if (!internal_hits.empty()) { internal_hits.clear(); // TODO: don't clear when using Optimum Search Schemes with lower error bounds - uint8_t const s = get(cfg).value; + uint8_t const s = get(cfg).value; max_error2.total += s - 1; detail::search_algo(index, query, max_error2, internal_delegate); } @@ -135,7 +138,7 @@ inline auto search_single(index_t const & index, query_t & query, configuration_ typename index_t::size_type>; std::vector hits; - if constexpr (cfg_t::template exists>()) + if constexpr (cfg_t::template exists>()) { // only one cursor is reported but it might contain more than one text position if (!internal_hits.empty()) @@ -177,6 +180,9 @@ inline auto search_single(index_t const & index, query_t & query, configuration_ * specified in `cfg` also has a strong exception guarantee; basic exception guarantee otherwise. */ template +//!\cond + requires configuration_t::template exists() +//!\endcond inline auto search_all(index_t const & index, queries_t & queries, configuration_t const & cfg) { using cfg_t = remove_cvref_t; @@ -209,6 +215,32 @@ inline auto search_all(index_t const & index, queries_t & queries, configuration } } +//!\overload +template +//!\cond + requires !configuration_t::template exists() +//!\endcond +inline auto search_all(index_t const & index, queries_t & queries, configuration_t const & cfg) +{ + using seqan3::get; + auto mode_config = get(cfg); + + if constexpr (decltype(mode_config)::has_dynamic_state) + { + return std::visit(multi_invocable + { + [&] (search_mode_all_best) { return search_all(index, queries, cfg | internal_search_mode{search_cfg::all_best}); }, + [&] (search_mode_best) { return search_all(index, queries, cfg | internal_search_mode{search_cfg::best}); }, + [&] (search_cfg::strata const & strata) { return search_all(index, queries, cfg | internal_search_mode{strata}); }, + [&] (auto) { return search_all(index, queries, cfg | internal_search_mode{search_cfg::all}); } + }, mode_config.search_modes()); + } + else + { + return search_all(index, queries, cfg | internal_search_mode{mode_config.value}); + } +} + //!\} } // namespace seqan3::detail diff --git a/test/unit/search/search_configuration_test.cpp b/test/unit/search/search_configuration_test.cpp index faa8fbac7be..f7b26f3038b 100644 --- a/test/unit/search/search_configuration_test.cpp +++ b/test/unit/search/search_configuration_test.cpp @@ -7,7 +7,7 @@ #include -#include +#include #include @@ -20,7 +20,8 @@ class search_configuration_test : public ::testing::Test using test_types = ::testing::Types, search_cfg::max_error<>, search_cfg::mode, - search_cfg::output>; + search_cfg::output + >; TYPED_TEST_CASE(search_configuration_test, test_types); @@ -53,6 +54,29 @@ TYPED_TEST(search_configuration_test, configuration_exists) EXPECT_TRUE(decltype(cfg)::template exists()); } +TYPED_TEST(search_configuration_test, configuration_exists_constexpr) +{ + constexpr configuration cfg{TypeParam{}}; + constexpr bool cfg_exists = decltype(cfg)::template exists(); + EXPECT_TRUE(cfg_exists); +} + +TEST(search_configuration_mode_test, select_at_runtime) +{ + { + seqan3::search_cfg::mode m{seqan3::search_cfg::all}; + EXPECT_FALSE(m.has_dynamic_state); + } + + { + seqan3::search_cfg::mode m{}; + EXPECT_TRUE(m.has_dynamic_state); + + m = seqan3::search_cfg::all_best; + EXPECT_TRUE(std::holds_alternative(m.search_modes())); + } +} + // TEST(search_configuration_test, illegal_runtime_configurations) // { // std::vector text{"ACGT"_dna4}, query{"ACG"_dna4}; diff --git a/test/unit/search/search_test.cpp b/test/unit/search/search_test.cpp index 796e03e0f1c..49060b8a8d8 100644 --- a/test/unit/search/search_test.cpp +++ b/test/unit/search/search_test.cpp @@ -300,6 +300,13 @@ TYPED_TEST(search_test, search_strategy_all) configuration const cfg = max_error{total{1}} | mode{all}; EXPECT_EQ(uniquify(search("ACGT"_dna4, this->index, cfg)), (hits_result_t{0, 1, 4, 5, 8, 9})); } + + { // Test with dynamic mode. + mode dynamic_mode{}; + dynamic_mode = all; + configuration const cfg = max_error{total{1}} | dynamic_mode; + EXPECT_EQ(uniquify(search("ACGT"_dna4, this->index, cfg)), (hits_result_t{0, 1, 4, 5, 8, 9})); + } } TYPED_TEST(search_test, search_strategy_best) @@ -317,6 +324,18 @@ TYPED_TEST(search_test, search_strategy_best) EXPECT_EQ(search("AAAA"_dna4, this->index, cfg), (hits_result_t{})); // no hit } + { // Test with dynamic mode. + mode dynamic_mode{}; + dynamic_mode = best; + configuration const cfg = max_error{total{1}} | dynamic_mode; + + hits_result_t result = search("ACGT"_dna4, this->index, cfg); + ASSERT_EQ(result.size(), 1u); + EXPECT_TRUE(std::find(possible_hits.begin(), possible_hits.end(), result[0]) != possible_hits.end()); + + EXPECT_EQ(search("AAAA"_dna4, this->index, cfg), (hits_result_t{})); // no hit + } + { // Find best match with 1 insertion at the end. configuration const cfg = max_error{total{1}, insertion{1}} | mode{best}; @@ -386,6 +405,16 @@ TYPED_TEST(search_test, search_strategy_all_best) EXPECT_EQ(search("AAAA"_dna4, this->index, cfg), (hits_result_t{})); // no hit } + + { // Test with dynamic mode. + mode dynamic_mode{}; + dynamic_mode = all_best; + configuration const cfg = max_error{total{1}} | dynamic_mode; + + EXPECT_EQ(uniquify(search("ACGT"_dna4, this->index, cfg)), (hits_result_t{0, 4, 8})); // 1, 5, 9 are not best hits + + EXPECT_EQ(search("AAAA"_dna4, this->index, cfg), (hits_result_t{})); // no hit + } } TYPED_TEST(search_test, search_strategy_strata) @@ -397,6 +426,13 @@ TYPED_TEST(search_test, search_strategy_strata) EXPECT_EQ(uniquify(search("ACGT"_dna4, this->index, cfg)), (hits_result_t{0, 4, 8})); } + { // Test with dynamic mode. + mode dynamic_mode{}; + dynamic_mode = strata{0}; + configuration const cfg = max_error{total{1}} | dynamic_mode; + EXPECT_EQ(uniquify(search("ACGT"_dna4, this->index, cfg)), (hits_result_t{0, 4, 8})); + } + { configuration const cfg = max_error{total{1}} | mode{strata{1}}; EXPECT_EQ(uniquify(search("ACGT"_dna4, this->index, cfg)), (hits_result_t{0, 1, 4, 5, 8, 9}));