SeqAn3 3.4.0-rc.4
The Modern C++ library for sequence analysis.
Loading...
Searching...
No Matches
alignment_configurator.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2006-2025 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2025 Knut Reinert & MPI für molekulare Genetik
3// SPDX-License-Identifier: BSD-3-Clause
4
10#pragma once
11
12#include <functional>
13#include <tuple>
14#include <utility>
15#include <vector>
16
43#include <seqan3/alignment/pairwise/policy/affine_gap_init_policy.hpp>
44#include <seqan3/alignment/pairwise/policy/affine_gap_policy.hpp>
45#include <seqan3/alignment/pairwise/policy/alignment_matrix_policy.hpp>
46#include <seqan3/alignment/pairwise/policy/find_optimum_policy.hpp>
47#include <seqan3/alignment/pairwise/policy/scoring_scheme_policy.hpp>
48#include <seqan3/alignment/pairwise/policy/simd_affine_gap_policy.hpp>
49#include <seqan3/alignment/pairwise/policy/simd_find_optimum_policy.hpp>
56#include <seqan3/utility/simd/simd.hpp>
61
62namespace seqan3::detail
63{
64
74template <typename range_type, typename alignment_config_type>
75struct alignment_contract
76{
77private:
82 using unref_range_type = std::remove_reference_t<range_type>;
84 using first_seq_t = std::tuple_element_t<0, std::ranges::range_value_t<unref_range_type>>;
86 using second_seq_t = std::tuple_element_t<1, std::ranges::range_value_t<unref_range_type>>;
88
89public:
91 static constexpr bool expects_tuple_like_value_type()
92 {
94 && std::tuple_size_v<std::ranges::range_value_t<unref_range_type>> == 2;
95 }
96
98 static constexpr bool expects_valid_scoring_scheme()
99 {
100 if constexpr (alignment_config_type::template exists<align_cfg::scoring_scheme>())
101 {
102 using scoring_type = std::remove_reference_t<
103 decltype(get<align_cfg::scoring_scheme>(std::declval<alignment_config_type>()).scheme)>;
104 return static_cast<bool>(scoring_scheme_for<scoring_type,
105 std::ranges::range_value_t<first_seq_t>,
106 std::ranges::range_value_t<second_seq_t>>);
107 }
108 else
109 {
110 return false;
111 }
112 }
113
115 static constexpr bool expects_alignment_configuration()
116 {
117 bool const is_global = alignment_config_type::template exists<seqan3::align_cfg::method_global>();
118 bool const is_local = alignment_config_type::template exists<seqan3::align_cfg::method_local>();
119
120 return (is_global || is_local);
121 }
122};
123
128struct alignment_configurator
129{
130private:
134 template <typename traits_t>
135 struct select_matrix_policy
136 {
137 private:
139 static constexpr bool only_coordinates =
140 !(traits_t::compute_begin_positions || traits_t::compute_sequence_alignment);
141
143 using score_matrix_t =
144 std::conditional_t<traits_t::is_banded,
145 alignment_score_matrix_one_column_banded<typename traits_t::score_type>,
146 alignment_score_matrix_one_column<typename traits_t::score_type>>;
148 using trace_matrix_t =
149 std::conditional_t<traits_t::is_banded,
150 alignment_trace_matrix_full_banded<typename traits_t::trace_type, only_coordinates>,
151 alignment_trace_matrix_full<typename traits_t::trace_type, only_coordinates>>;
152
153 public:
155 using type = deferred_crtp_base<alignment_matrix_policy, score_matrix_t, trace_matrix_t>;
156 };
157
161 template <typename traits_t>
162 struct select_gap_policy
163 {
164 private:
166 using score_t = typename traits_t::score_type;
169
170 public:
172 using type = std::conditional_t<traits_t::is_vectorised,
173 deferred_crtp_base<simd_affine_gap_policy, score_t, is_local_t>,
174 deferred_crtp_base<affine_gap_policy, score_t, is_local_t>>;
175 };
176
183 template <typename traits_t>
184 struct select_find_optimum_policy
185 {
186 private:
188 using score_t = typename traits_t::score_type;
189
190 public:
192 using type = std::conditional_t<traits_t::is_vectorised,
193 deferred_crtp_base<simd_find_optimum_policy, score_t>,
194 deferred_crtp_base<find_optimum_policy>>;
195 };
196
198 template <typename traits_t, typename... args_t>
199 using select_alignment_algorithm_t = lazy_conditional_t<traits_t::is_banded,
200 lazy<pairwise_alignment_algorithm_banded, args_t...>,
201 lazy<pairwise_alignment_algorithm, args_t...>>;
202
206 template <typename config_t>
207 struct select_gap_recursion_policy
208 {
209 private:
211 using traits_type = alignment_configuration_traits<config_t>;
213 static constexpr bool with_trace = traits_type::requires_trace_information;
214
216 using gap_recursion_policy_type = std::conditional_t<with_trace,
217 policy_affine_gap_with_trace_recursion<config_t>,
218 policy_affine_gap_recursion<config_t>>;
220 using banded_gap_recursion_policy_type =
221 std::conditional_t<with_trace,
222 policy_affine_gap_with_trace_recursion_banded<config_t>,
223 policy_affine_gap_recursion_banded<config_t>>;
224
225 public:
227 using type =
229 };
230
231public:
258 template <align_pairwise_range_input sequences_t, typename config_t>
259 requires is_type_specialisation_of_v<config_t, configuration>
260 static constexpr auto configure(config_t const & cfg)
261 {
262 auto config_with_output = maybe_default_output(cfg);
263 using config_with_output_t = decltype(config_with_output);
264
265 // ----------------------------------------------------------------------------
266 // Configure the type-erased alignment function.
267 // ----------------------------------------------------------------------------
268
269 using first_seq_t = std::tuple_element_t<0, std::ranges::range_value_t<sequences_t>>;
270 using second_seq_t = std::tuple_element_t<1, std::ranges::range_value_t<sequences_t>>;
271
272 using wrapped_first_t = type_reduce_t<first_seq_t &>;
273 using wrapped_second_t = type_reduce_t<second_seq_t &>;
274
275 // The alignment executor passes a chunk over an indexed sequence pair range to the alignment algorithm.
276 using indexed_sequence_pair_range_t = typename chunked_indexed_sequence_pairs<sequences_t>::type;
277 using indexed_sequence_pair_chunk_t = std::ranges::range_value_t<indexed_sequence_pair_range_t>;
278
279 // Select the result type based on the sequences and the configuration.
280 using alignment_result_value_t = typename align_result_selector<std::remove_reference_t<wrapped_first_t>,
282 config_with_output_t>::type;
283 using alignment_result_t = alignment_result<alignment_result_value_t>;
284 using callback_on_result_t = std::function<void(alignment_result_t)>;
285 // Define the function wrapper type.
286 using function_wrapper_t = std::function<void(indexed_sequence_pair_chunk_t, callback_on_result_t)>;
287
288 // Capture the alignment result type.
289 auto config_with_result_type = config_with_output | align_cfg::detail::result_type<alignment_result_t>{};
290
291 // ----------------------------------------------------------------------------
292 // Test some basic preconditions
293 // ----------------------------------------------------------------------------
294
295 using alignment_contract_t = alignment_contract<sequences_t, config_with_output_t>;
296
297 static_assert(alignment_contract_t::expects_alignment_configuration(),
298 "Alignment configuration error: "
299 "The alignment can only be configured with alignment configurations.");
300
301 static_assert(alignment_contract_t::expects_tuple_like_value_type(),
302 "Alignment configuration error: "
303 "The value type of the sequence ranges must model the seqan3::tuple_like and must contain "
304 "exactly 2 elements.");
305
306 static_assert(alignment_contract_t::expects_valid_scoring_scheme(),
307 "Alignment configuration error: "
308 "Either the scoring scheme was not configured or the given scoring scheme cannot be invoked with "
309 "the value types of the passed sequences.");
310
311 // ----------------------------------------------------------------------------
312 // Configure the algorithm
313 // ----------------------------------------------------------------------------
314
315 // Use default edit distance if gaps are not set.
316 align_cfg::gap_cost_affine edit_gap_cost{};
317 auto const & gap_cost = config_with_result_type.get_or(edit_gap_cost);
318 auto const & scoring_scheme = get<align_cfg::scoring_scheme>(cfg).scheme;
319
320 if constexpr (config_t::template exists<seqan3::align_cfg::method_global>())
321 {
322 // Only use edit distance if ...
323 if constexpr (std::same_as<std::remove_cvref_t<decltype(scoring_scheme)>, hamming_scoring_scheme>)
324 {
325 auto method_global_cfg = get<seqan3::align_cfg::method_global>(config_with_result_type);
326
327 bool const has_edit_distance_gaps = gap_cost.open_score == 0 && gap_cost.extension_score == -1;
328 bool const has_no_free_end_gaps_sequence2 = !method_global_cfg.free_end_gaps_sequence2_leading
329 && !method_global_cfg.free_end_gaps_sequence2_trailing;
330 bool const has_equal_free_end_gaps_sequence1 = method_global_cfg.free_end_gaps_sequence1_leading
331 == method_global_cfg.free_end_gaps_sequence1_trailing;
332
333 if (has_edit_distance_gaps && has_no_free_end_gaps_sequence2 && has_equal_free_end_gaps_sequence1)
334 {
335 return std::pair{configure_edit_distance<function_wrapper_t>(config_with_result_type),
336 config_with_result_type};
337 }
338 }
339 }
340
341 // ----------------------------------------------------------------------------
342 // Check if invalid configuration was used.
343 // ----------------------------------------------------------------------------
344
345 // Do not allow min score configuration for alignments not computing the edit distance.
346 if (config_t::template exists<align_cfg::min_score>())
347 throw invalid_alignment_configuration{"The align_cfg::min_score configuration is only allowed for the "
348 "specific edit distance computation."};
349 // Configure the alignment algorithm.
350 return std::pair{configure_scoring_scheme<function_wrapper_t>(config_with_result_type),
351 config_with_result_type};
352 }
353
354private:
364 template <typename config_t>
365 static constexpr auto maybe_default_output(config_t const & config) noexcept
366 {
367 using traits_t = alignment_configuration_traits<config_t>;
368
369 if constexpr (traits_t::has_output_configuration)
370 return config;
371 else
372 return config | align_cfg::output_score{} | align_cfg::output_begin_position{}
373 | align_cfg::output_end_position{} | align_cfg::output_alignment{} | align_cfg::output_sequence1_id{}
374 | align_cfg::output_sequence2_id{};
375 }
376
382 template <typename function_wrapper_t, typename config_t>
383 static constexpr function_wrapper_t configure_edit_distance(config_t const & cfg)
384 {
385 using traits_t = alignment_configuration_traits<config_t>;
386
387 // ----------------------------------------------------------------------------
388 // Unsupported configurations
389 // ----------------------------------------------------------------------------
390
391 if constexpr (traits_t::is_banded)
392 throw invalid_alignment_configuration{"Banded alignments are yet not supported."};
393
394 // ----------------------------------------------------------------------------
395 // Configure semi-global alignment
396 // ----------------------------------------------------------------------------
397
398 // Get the value for the sequence ends configuration.
399 auto method_global_cfg = cfg.get_or(align_cfg::method_global{});
400
401 auto configure_edit_traits = [&](auto is_semi_global)
402 {
403 struct edit_traits_type
404 {
405 using is_semi_global_type [[maybe_unused]] = std::remove_cvref_t<decltype(is_semi_global)>;
406 };
407
408 edit_distance_algorithm<std::remove_cvref_t<config_t>, edit_traits_type> algorithm{cfg};
409 return function_wrapper_t{std::move(algorithm)};
410 };
411
412 // Check if it has free ends set for the first sequence trailing gaps.
413 auto has_free_ends_trailing = [&](auto first) constexpr
414 {
415 if constexpr (!decltype(first)::value)
416 {
417 return configure_edit_traits(std::false_type{});
418 }
419 else // Resolve correct property at runtime.
420 {
421 if (method_global_cfg.free_end_gaps_sequence1_trailing)
422 return configure_edit_traits(std::true_type{});
423 else
424 return configure_edit_traits(std::false_type{});
425 }
426 };
427
428 // Check if it has free ends set for the first sequence leading gaps.
429 if (method_global_cfg.free_end_gaps_sequence1_leading)
430 return has_free_ends_trailing(std::true_type{});
431 else
432 return has_free_ends_trailing(std::false_type{});
433 }
434
451 template <typename function_wrapper_t, typename config_t>
452 static constexpr function_wrapper_t configure_scoring_scheme(config_t const & cfg);
453
468 template <typename function_wrapper_t, typename... policies_t, typename config_t>
469 static constexpr function_wrapper_t make_algorithm(config_t const & cfg)
470 {
471 using traits_t = alignment_configuration_traits<config_t>;
472
473 // Temporarily we will use the new and the old alignment implementation in order to
474 // refactor step-by-step to the new implementation. The new implementation will be tested in
475 // macrobenchmarks to show that it maintains a high performance.
476
477 // Use old alignment implementation if...
478 if constexpr (traits_t::is_local || // it is a local alignment,
479 traits_t::is_debug || // it runs in debug mode,
480 traits_t::compute_sequence_alignment || // it computes more than the begin position.
481 (traits_t::is_banded && traits_t::compute_begin_positions)
482 || // banded && more than end positions.
483 (traits_t::is_vectorised && traits_t::compute_end_positions)) // simd and more than the score.
484 {
485 using matrix_policy_t = typename select_matrix_policy<traits_t>::type;
486 using gap_policy_t = typename select_gap_policy<traits_t>::type;
487 using find_optimum_t = typename select_find_optimum_policy<traits_t>::type;
488 using gap_init_policy_t = deferred_crtp_base<affine_gap_init_policy>;
489
490 return alignment_algorithm<config_t,
491 matrix_policy_t,
492 gap_policy_t,
493 find_optimum_t,
494 gap_init_policy_t,
495 policies_t...>{cfg};
496 }
497 else // Use new alignment algorithm implementation.
498 {
499 //----------------------------------------------------------------------------------------------------------
500 // Configure the optimum tracker policy.
501 //----------------------------------------------------------------------------------------------------------
502
503 using scalar_optimum_updater_t =
505
506 using optimum_tracker_policy_t =
507 lazy_conditional_t<traits_t::is_vectorised,
508 lazy<policy_optimum_tracker_simd, config_t, max_score_updater_simd_global>,
509 lazy<policy_optimum_tracker, config_t, scalar_optimum_updater_t>>;
510
511 //----------------------------------------------------------------------------------------------------------
512 // Configure the gap scheme policy.
513 //----------------------------------------------------------------------------------------------------------
514
515 using gap_cost_policy_t = typename select_gap_recursion_policy<config_t>::type;
516
517 //----------------------------------------------------------------------------------------------------------
518 // Configure the result builder policy.
519 //----------------------------------------------------------------------------------------------------------
520
521 using result_builder_policy_t = policy_alignment_result_builder<config_t>;
522
523 //----------------------------------------------------------------------------------------------------------
524 // Configure the scoring scheme policy.
525 //----------------------------------------------------------------------------------------------------------
526
527 using alignment_method_t = std::
528 conditional_t<traits_t::is_global, seqan3::align_cfg::method_global, seqan3::align_cfg::method_local>;
529
530 using score_t = typename traits_t::score_type;
531 using scoring_scheme_t = typename traits_t::scoring_scheme_type;
532 constexpr bool is_aminoacid_scheme =
533 is_type_specialisation_of_v<scoring_scheme_t, aminoacid_scoring_scheme>;
534
535 using simple_simd_scheme_t = lazy_conditional_t<traits_t::is_vectorised,
536 lazy<simd_match_mismatch_scoring_scheme,
537 score_t,
538 typename traits_t::scoring_scheme_alphabet_type,
539 alignment_method_t>,
540 void>;
541 using matrix_simd_scheme_t = lazy_conditional_t<traits_t::is_vectorised,
542 lazy<simd_matrix_scoring_scheme,
543 score_t,
544 typename traits_t::scoring_scheme_alphabet_type,
545 alignment_method_t>,
546 void>;
547
548 using alignment_scoring_scheme_t =
549 std::conditional_t<traits_t::is_vectorised,
551 scoring_scheme_t>;
552
553 using scoring_scheme_policy_t = policy_scoring_scheme<config_t, alignment_scoring_scheme_t>;
554
555 //----------------------------------------------------------------------------------------------------------
556 // Configure the alignment matrix policy.
557 //----------------------------------------------------------------------------------------------------------
558
559 using score_matrix_t = score_matrix_single_column<score_t>;
560 using trace_matrix_t = trace_matrix_full<trace_directions>;
561
562 using alignment_matrix_t =
563 std::conditional_t<traits_t::requires_trace_information,
564 combined_score_and_trace_matrix<score_matrix_t, trace_matrix_t>,
565 score_matrix_t>;
566 using alignment_matrix_policy_t = policy_alignment_matrix<traits_t, alignment_matrix_t>;
567
568 //----------------------------------------------------------------------------------------------------------
569 // Configure the final alignment algorithm.
570 //----------------------------------------------------------------------------------------------------------
571
572 using algorithm_t = select_alignment_algorithm_t<traits_t,
573 config_t,
574 gap_cost_policy_t,
575 optimum_tracker_policy_t,
576 result_builder_policy_t,
577 scoring_scheme_policy_t,
578 alignment_matrix_policy_t>;
579 return algorithm_t{cfg};
580 }
581 }
582};
583
585template <typename function_wrapper_t, typename config_t>
586constexpr function_wrapper_t alignment_configurator::configure_scoring_scheme(config_t const & cfg)
587{
588 using traits_t = alignment_configuration_traits<config_t>;
589
590 using scoring_scheme_t = typename traits_t::scoring_scheme_type;
591 constexpr bool is_aminoacid_scheme = is_type_specialisation_of_v<scoring_scheme_t, aminoacid_scoring_scheme>;
592 using alignment_type_t = typename std::
593 conditional_t<traits_t::is_global, seqan3::align_cfg::method_global, seqan3::align_cfg::method_local>;
594
595 using simple_simd_scheme_t = lazy_conditional_t<traits_t::is_vectorised,
596 lazy<simd_match_mismatch_scoring_scheme,
597 typename traits_t::score_type,
598 typename traits_t::scoring_scheme_alphabet_type,
599 alignment_type_t>,
600 void>;
601 using matrix_simd_scheme_t = lazy_conditional_t<traits_t::is_vectorised,
602 lazy<simd_matrix_scoring_scheme,
603 typename traits_t::score_type,
604 typename traits_t::scoring_scheme_alphabet_type,
605 alignment_type_t>,
606 void>;
607
608 using alignment_scoring_scheme_t =
609 std::conditional_t<traits_t::is_vectorised,
611 scoring_scheme_t>;
612
613 using scoring_scheme_policy_t = deferred_crtp_base<scoring_scheme_policy, alignment_scoring_scheme_t>;
614 return make_algorithm<function_wrapper_t, scoring_scheme_policy_t>(cfg);
615}
617} // namespace seqan3::detail
Provides configuration for alignment output.
Provides seqan3::align_cfg::detail::result_type.
Provides seqan3::detail::align_result_selector.
Provides concepts needed internally for the alignment algorithms.
Provides helper type traits for the configuration and execution of the alignment algorithm.
Provides seqan3::detail::alignment_algorithm.
Provides seqan3::alignment_result.
Provides seqan3::detail::alignment_score_matrix_one_column.
Provides seqan3::detail::alignment_score_matrix_one_column_banded.
Provides seqan3::detail::alignment_trace_matrix_full.
Provides seqan3::detail::alignment_trace_matrix_full_banded.
Provides seqan3::aminoacid_scoring_scheme.
Provides seqan3::detail::combined_score_and_trace_matrix.
Provides seqan3::detail::deferred_crtp_base.
Provides seqan3::detail::edit_distance_algorithm.
A concept that requires that type be able to score two letters.
Whether a type behaves like a tuple.
Provides lazy template instantiation traits.
Provides seqan3::nucleotide_scoring_scheme.
Provides seqan3::detail::pairwise_alignment_algorithm.
Provides seqan3::detail::pairwise_alignment_algorithm.
Provides seqan3::detail::policy_affine_gap_recursion.
Provides seqan3::detail::policy_affine_gap_recursion_banded.
Provides seqan3::detail::policy_affine_gap_with_trace_recursion.
Provides seqan3::detail::policy_affine_gap_with_trace_recursion_banded.
Provides seqan3::detail::policy_alignment_matrix.
Provides seqan3::detail::policy_alignment_result_builder.
Provides seqan3::detail::policy_optimum_tracker.
Provides seqan3::detail::policy_optimum_tracker_simd.
Provides seqan3::detail::policy_scoring_scheme.
Provides seqan3::detail::score_matrix_single_column.
Provides seqan3::detail::simd_match_mismatch_scoring_scheme.
Provides seqan3::detail::simd_matrix_scoring_scheme.
Provides type traits for working with templates.
Provides seqan3::detail::trace_matrix_full.
Provides seqan3::views::type_reduce.
Provides seqan3::tuple_like.
Provides seqan3::views::zip.
Hide me