Skip to content

Commit 1bbd1eb

Browse files
authored
Merge pull request #2019 from rrahn/search_confguration/on_result_release
Search confguration/on result release
2 parents bf04354 + 473a8eb commit 1bbd1eb

19 files changed

Lines changed: 387 additions & 77 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ Note that 3.1.0 will be the first API stable release and interfaces in this rele
7171
multiple bins ([\#920](https://github.com/seqan/seqan3/pull/920)).
7272
* Added `seqan3::search_cfg::hit`, which allows dynamic configuration of the hit strategy.
7373
([\#1853](https://github.com/seqan/seqan3/pull/1853)).
74+
* Added `seqan3::search_cfg::on_result`, which allows providing a custom callback for the search algorithm
75+
([\#2019](https://github.com/seqan/seqan3/pull/2019)).
7476

7577
## API changes
7678

include/seqan3/alignment/configuration/align_config_on_result.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414

1515
#include <type_traits>
1616

17-
#include <range/v3/utility/semiregular_box.hpp>
1817

1918
#include <seqan3/alignment/configuration/detail.hpp>
2019
#include <seqan3/core/algorithm/pipeable_config_element.hpp>
20+
#include <seqan3/core/semiregular_box.hpp>
2121

2222
namespace seqan3::align_cfg
2323
{
@@ -39,7 +39,7 @@ namespace seqan3::align_cfg
3939
* function, you need to make sure that the referenced function object outlives the call to the alignment algorithm.
4040
*
4141
* \if DEV
42-
* The given callback is wrapped inside a ranges::semiregular_box wrapper type. This allows to also
42+
* The given callback is wrapped inside a seqan3::semiregular_box wrapper type. This allows to also
4343
* use lambdas with a capture block, which otherwise are not std::copy_assignable and therefore invalidate the
4444
* requirements for the configuration element (must model std::semiregular).
4545
* \endif
@@ -54,7 +54,7 @@ template <std::move_constructible callback_t>
5454
struct on_result : public seqan3::pipeable_config_element<on_result<callback_t>>
5555
{
5656
//!\brief The stored callable which will be invoked with the alignment result.
57-
ranges::semiregular_box_t<callback_t> callback{}; // Allow lambdas with capture block which are not copy_assignable.
57+
seqan3::semiregular_box_t<callback_t> callback{}; // Allow lambdas with capture block which are not copy_assignable.
5858

5959
/*!\name Constructors, destructor and assignment
6060
* \{
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// -----------------------------------------------------------------------------------------------------
2+
// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin
3+
// Copyright (c) 2016-2019, 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+
8+
/*!\file
9+
* \brief Provides seqan3::semiregular_box.
10+
* \author Rene Rahn <rene.rahn AT fu-berlin.de>
11+
*/
12+
13+
#pragma once
14+
15+
#include <range/v3/utility/semiregular_box.hpp>
16+
17+
#include <seqan3/core/platform.hpp>
18+
19+
namespace seqan3
20+
{
21+
/*!\brief Utility wrapper that behaves like std::optional but makes the type conform with the std::semiregular concept.
22+
Imported from ranges::semiregular_box.
23+
* \ingroup core
24+
*
25+
* \see https://en.cppreference.com/w/cpp/ranges/semiregular_wrapper
26+
*/
27+
using SEQAN3_DOXYGEN_ONLY(semiregular_box =) ::ranges::semiregular_box;
28+
29+
/*!\brief Utility transformation trait to get a wrapper type that models std::semiregular. Imported from
30+
ranges::semiregular_box_t.
31+
* \ingroup core
32+
*
33+
* \see https://en.cppreference.com/w/cpp/ranges/semiregular_wrapper
34+
*/
35+
using SEQAN3_DOXYGEN_ONLY(semiregular_box_t =) ::ranges::semiregular_box_t;
36+
37+
} // namespace seqan3

include/seqan3/range/views/take_until.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#pragma once
1414

15+
#include <seqan3/core/semiregular_box.hpp>
1516
#include <seqan3/core/type_traits/iterator.hpp>
1617
#include <seqan3/core/type_traits/range.hpp>
1718
#include <seqan3/core/type_traits/transformation_trait_or.hpp>
@@ -209,7 +210,7 @@ class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_iterator :
209210
std::remove_reference_t<fun_t> const &,
210211
std::remove_reference_t<fun_t> &>;
211212
//!\brief Reference to the functor stored in the view.
212-
ranges::semiregular_box_t<fun_ref_t> fun;
213+
seqan3::semiregular_box_t<fun_ref_t> fun;
213214

214215
public:
215216
/*!\name Constructors, destructor and assignment
@@ -283,7 +284,7 @@ class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_consume_itera
283284
std::remove_reference_t<fun_t> const &,
284285
std::remove_reference_t<fun_t> &>;
285286
//!\brief Reference to the functor stored in the view.
286-
ranges::semiregular_box_t<fun_ref_t> fun;
287+
seqan3::semiregular_box_t<fun_ref_t> fun;
287288

288289
//!\brief The sentinel type is identical to that of the underlying range.
289290
using sentinel_type = std::ranges::sentinel_t<rng_t>;
@@ -428,7 +429,7 @@ class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_sentinel
428429
urng_sentinel_type urng_sentinel{};
429430

430431
//!\brief Reference to the predicate stored in the view.
431-
ranges::semiregular_box_t<predicate_ref_t> predicate{};
432+
seqan3::semiregular_box_t<predicate_ref_t> predicate{};
432433

433434
public:
434435
/*!\name Constructors, destructor and assignment

include/seqan3/search/configuration/all.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,20 @@
163163
* The seqan3::search_cfg::parallel configuration element can be combined with any other search configuration.
164164
*
165165
* \include test/snippet/search/configuration_parallel.cpp
166+
*
167+
* ### User callback
168+
*
169+
* In the default case, a call to seqan3::search returns a lazy range over the results of the search. This lazy range
170+
* has the advantage that the results are always in a deterministic order even if the search is executed in parallel.
171+
* Sometimes, however, it might be desirable to provide a user defined callback.
172+
* To do so, one can use the configuration element seqan3::search_cfg::on_result. This configuration element
173+
* is initialised with a user defined callback, e.g. a lambda function, which will be invoked with a generated
174+
* seqan3::search_result whenever a hit was found.
175+
* This has two implications. First, the return type of the seqan3::search function changes to `void`, i.e. it
176+
* returns nothing. Second, in a parallel execution of the search, the order of the hits is not deterministic and the
177+
* user has to make sure that concurrent invocations of the given callback are safe.
178+
*
179+
* The following snippet demonstrates the basic use case for this configuration element:
180+
*
181+
* \include test/snippet/search/search_with_user_callback.cpp
166182
*/

include/seqan3/search/configuration/detail.hpp

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ enum struct search_config_id : uint8_t
4444
max_error_substitution, //!< Identifier for the max_error_substitution configuration.
4545
max_error_insertion, //!< Identifier for the max_error_insertion configuration.
4646
max_error_deletion, //!< Identifier for the max_error_deletion configuration.
47+
on_result, //!< Identifier for the configuration to pass a user defined callable.
4748
output_query_id, //!< Identifier for the output configuration of the query_id.
4849
output_reference_id, //!< Identifier for the output configuration of the reference_id.
4950
output_reference_begin_position, //!< Identifier for the output configuration of the reference_begin_position.
@@ -80,24 +81,26 @@ inline constexpr std::array<std::array<bool, static_cast<uint8_t>(search_config_
8081
// | max_error_substitution,
8182
// | | max_error_insertion,
8283
// | | | max_error_deletion,
83-
// | | | | output_query_id,
84-
// | | | | | output_reference_id,
85-
// | | | | | | output_reference_begin_position,
86-
// | | | | | | | output_index_cursor,
87-
// | | | | | | | | hit,
88-
// | | | | | | | | | parallel,
89-
// | | | | | | | | | | result_type
90-
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // max_error_total
91-
{ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // max_error_substitution
92-
{ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1}, // max_error_insertion
93-
{ 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1}, // max_error_deletion
94-
{ 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1}, // output_query_id
95-
{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}, // output_reference_id
96-
{ 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1}, // output_reference_begin_position
97-
{ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}, // output_index_cursor
98-
{ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1}, // hit
99-
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, // parallel
100-
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0} // result_type
84+
// | | | | on_result,
85+
// | | | | | output_query_id,
86+
// | | | | | | output_reference_id,
87+
// | | | | | | | output_reference_begin_position,
88+
// | | | | | | | | output_index_cursor,
89+
// | | | | | | | | | hit,
90+
// | | | | | | | | | | parallel,
91+
// | | | | | | | | | | | result_type
92+
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // max_error_total
93+
{ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // max_error_substitution
94+
{ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // max_error_insertion
95+
{ 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1}, // max_error_deletion
96+
{ 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1}, // on_result
97+
{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1}, // output_query_id
98+
{ 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}, // output_reference_id
99+
{ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1}, // output_reference_begin_position
100+
{ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}, // output_index_cursor
101+
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1}, // hit
102+
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, // parallel
103+
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0} // result_type
101104
}
102105
};
103106

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// -----------------------------------------------------------------------------------------------------
2+
// Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
3+
// Copyright (c) 2016-2020, 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+
8+
/*!\file
9+
* \brief Provides seqan3::search_cfg::on_result.
10+
* \author Rene Rahn <rene.rahn AT fu-berlin.de>
11+
*/
12+
13+
#pragma once
14+
15+
#include <type_traits>
16+
17+
18+
#include <seqan3/core/algorithm/pipeable_config_element.hpp>
19+
#include <seqan3/core/semiregular_box.hpp>
20+
#include <seqan3/search/configuration/detail.hpp>
21+
22+
namespace seqan3::search_cfg
23+
{
24+
25+
/*!\brief Configuration element to provide a user defined callback function for the search.
26+
* \ingroup search_configuration
27+
*
28+
* \tparam callback_t The type of the callback; must model std::invocable with the generated seqan3::search_result
29+
* and std::move_constructible.
30+
*
31+
* \details
32+
*
33+
* Allows the user to specify a callback that should be called for every computed search result. The callback
34+
* must take exactly one argument for the search result and return `void`. If the user callback is
35+
* specified, the call to the search algorithm seqan3::search will return nothing, i.e. it does not return
36+
* a seqan3::algorithm_result_generator_range any more. Note that within a parallel configuration, the order of the
37+
* generated search results and therefore the call to the user callback is non-deterministic.
38+
* However, the continuation interface with the
39+
* user callback can be more efficient in a concurrent environment.
40+
*
41+
* \if DEV
42+
* The given callback is wrapped inside a seqan3::semiregular_box wrapper type. This allows to also
43+
* use lambdas with a capture block, which otherwise are not std::copy_assignable and therefore invalidate the
44+
* requirements for the configuration element (must model std::semiregular).
45+
* \endif
46+
*
47+
* ### Example
48+
*
49+
* The following code snippet demonstrates the basic usage:
50+
*
51+
* \include test/snippet/search/configuration_on_result.cpp
52+
*/
53+
template <std::move_constructible callback_t>
54+
struct on_result : public seqan3::pipeable_config_element<on_result<callback_t>>
55+
{
56+
//!\brief The stored callable which will be invoked with the search result.
57+
seqan3::semiregular_box_t<callback_t> callback{}; // Allow lambdas with capture block which are not copy_assignable.
58+
59+
/*!\name Constructors, destructor and assignment
60+
* \{
61+
*/
62+
constexpr on_result() = default; //!< Defaulted.
63+
constexpr on_result(on_result const &) = default; //!< Defaulted.
64+
constexpr on_result(on_result &&) = default; //!< Defaulted.
65+
constexpr on_result & operator=(on_result const &) = default; //!< Defaulted.
66+
constexpr on_result & operator=(on_result &&) = default; //!< Defaulted.
67+
~on_result() = default; //!< Defaulted.
68+
69+
/*!\brief Constructs the configuration element with the given user callback.
70+
* \param[in] callback The callback to invoke with a computed seqan3::search_result.
71+
*/
72+
constexpr explicit on_result(callback_t callback) : callback{std::forward<callback_t>(callback)}
73+
{}
74+
//!\}
75+
76+
//!\privatesection
77+
//!\brief Internal id to check for consistent configuration settings.
78+
static constexpr seqan3::detail::search_config_id id{seqan3::detail::search_config_id::on_result};
79+
};
80+
81+
/*!\name Type deduction guides
82+
* \{
83+
*/
84+
//!\brief Deduces the callback type from a forwarding constructor argument.
85+
template <std::move_constructible callback_t>
86+
on_result(callback_t &&) -> on_result<std::decay_t<callback_t>>;
87+
//!\}
88+
} // namespace seqan3::search_cfg

include/seqan3/search/detail/policy_max_error.hpp

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,38 +26,81 @@ namespace seqan3::detail
2626
struct policy_max_error
2727
{
2828
protected:
29+
//!\brief The total errors set by the user.
30+
search_cfg::max_error_total total{};
31+
//!\brief The substitution errors set by the user.
32+
search_cfg::max_error_substitution substitution{};
33+
//!\brief The insertion errors set by the user.
34+
search_cfg::max_error_insertion insertion{};
35+
//!\brief The deletion errors set by the user.
36+
search_cfg::max_error_deletion deletion{};
37+
38+
//!\brief Flag indicating if only max error was given.
39+
bool only_max_error_total{false};
40+
//!\brief Flag indicating if max error total was given.
41+
bool has_max_error_total{false};
42+
43+
/*!\name Constructors, destructor and assignment
44+
* \{
45+
*/
46+
policy_max_error() = default; //!< Defaulted.
47+
policy_max_error(policy_max_error const &) = default; //!< Defaulted.
48+
policy_max_error(policy_max_error &&) = default; //!< Defaulted.
49+
policy_max_error & operator=(policy_max_error const &) = default; //!< Defaulted.
50+
policy_max_error & operator=(policy_max_error &&) = default; //!< Defaulted.
51+
~policy_max_error() = default; //!< Defaulted.
52+
53+
/*!\brief Initialises the policy with the given configuration.
54+
*
55+
* \tparam configuration_t The configuration type; must be an instance of seqan3::configuration.
56+
* \param[in] config The search configuration object.
57+
*
58+
* \details
59+
*
60+
* Initialises the maximal errors for the respective edit operations from the given configuration.
61+
*/
62+
template <typename configuration_t>
63+
//!\cond
64+
requires is_type_specialisation_of_v<configuration_t, seqan3::configuration>
65+
//!\endcond
66+
explicit policy_max_error(configuration_t const & config)
67+
{
68+
using search_traits_t = search_traits<configuration_t>;
69+
only_max_error_total = search_traits_t::only_max_error_total;
70+
has_max_error_total = search_traits_t::has_max_error_total;
71+
72+
total = config.get_or(search_cfg::max_error_total{search_cfg::error_count{0}});
73+
substitution = config.get_or(search_cfg::max_error_substitution{search_cfg::error_count{0}});
74+
insertion = config.get_or(search_cfg::max_error_insertion{search_cfg::error_count{0}});
75+
deletion = config.get_or(search_cfg::max_error_deletion{search_cfg::error_count{0}});
76+
}
77+
//!\}
78+
2979
/*!\brief Returns a detail::search_param object filled by the information from the configuration.
3080
*
31-
* \tparam configuration_t The search configuration type.
3281
* \tparam query_t Must model std::ranges::forward_range over the index's alphabet.
3382
*
34-
* \param[in] cfg The configuration object.
3583
* \param[in] query The current query sequence.
3684
*
3785
* \throws std::invalid_argument
3886
*/
39-
template <typename configuration_t, std::ranges::forward_range query_t>
40-
auto max_error_counts(configuration_t const & cfg, query_t && query)
87+
template <std::ranges::forward_range query_t>
88+
auto max_error_counts(query_t && query)
4189
{
42-
using search_traits_t = search_traits<configuration_t>;
43-
4490
detail::search_param errors{0, 0, 0, 0}; // total, substitution, insertion, deletion
4591

4692
[[maybe_unused]] auto query_size = std::ranges::size(query);
4793

48-
search_cfg::error_count const zero_error = search_cfg::error_count{0};
49-
50-
errors.total = to_error_count(cfg.get_or(search_cfg::max_error_total{zero_error}).value, query_size);
51-
errors.substitution = to_error_count(cfg.get_or(search_cfg::max_error_substitution{zero_error}).value,
52-
query_size);
53-
errors.insertion = to_error_count(cfg.get_or(search_cfg::max_error_insertion{zero_error}).value, query_size);
54-
errors.deletion = to_error_count(cfg.get_or(search_cfg::max_error_deletion{zero_error}).value, query_size);
94+
errors.total = to_error_count(total.value, query_size);
95+
errors.substitution = to_error_count(substitution.value, query_size);
96+
errors.insertion = to_error_count(insertion.value, query_size);
97+
errors.deletion = to_error_count(deletion.value, query_size);
5598

5699
// If only total is set, we set all other errors to the total limit.
57-
if constexpr (search_traits_t::only_max_error_total)
100+
if (only_max_error_total)
58101
errors.substitution = errors.insertion = errors.deletion = errors.total;
59102
// If total is not set but any other field is set than use total as the sum of all set errors.
60-
else if constexpr (!search_traits_t::has_max_error_total)
103+
else if (!has_max_error_total)
61104
errors.total = std::min<uint32_t>(255, errors.substitution + errors.insertion + errors.deletion);
62105

63106
// Validate the error configuration.

0 commit comments

Comments
 (0)