SeqAn3  3.0.1
The Modern C++ library for sequence analysis.
alignment_configurator.hpp
Go to the documentation of this file.
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 
13 #pragma once
14 
15 #include <functional>
16 #include <tuple>
17 #include <utility>
18 #include <vector>
19 
25 #include <seqan3/alignment/pairwise/policy/all.hpp>
41 
42 namespace seqan3::detail
43 {
44 
52 template <typename config_t>
54  requires is_type_specialisation_of_v<config_t, configuration> && config_t::template exists<align_cfg::result>()
56 struct align_config_result_score
57 {
58 private:
60  using result_config_t = std::remove_reference_t<decltype(seqan3::get<align_cfg::result>(std::declval<config_t>()))>;
61 
62 public:
64  using type = typename result_config_t::score_type;
65 };
66 
76 template <typename range_type,
77  typename alignment_config_type>
78 struct alignment_contract
79 {
80 private:
84  using unref_range_type = std::remove_reference_t<range_type>;
87  using first_seq_t = std::tuple_element_t<0, value_type_t<std::ranges::iterator_t<unref_range_type>>>;
89  using second_seq_t = std::tuple_element_t<1, value_type_t<std::ranges::iterator_t<unref_range_type>>>;
91 
92 public:
94  constexpr static bool expects_tuple_like_value_type()
95  {
97  std::tuple_size_v<value_type_t<std::ranges::iterator_t<unref_range_type>>> == 2;
98  }
99 
101  constexpr static bool expects_valid_scoring_scheme()
102  {
103  if constexpr (alignment_config_type::template exists<align_cfg::scoring>())
104  {
105  using scoring_type = std::remove_reference_t<
106  decltype(get<align_cfg::scoring>(std::declval<alignment_config_type>()).value)
107  >;
108  return static_cast<bool>(scoring_scheme<scoring_type,
109  value_type_t<first_seq_t>,
110  value_type_t<second_seq_t>>);
111  }
112  else
113  {
114  return false;
115  }
116  }
117 
119  constexpr static bool expects_alignment_configuration()
120  {
121  return alignment_config_type::template exists<align_cfg::mode>();
122  }
123 };
124 
129 struct alignment_configurator
130 {
131 private:
132 
136  template <typename traits_t>
137  struct select_matrix_policy
138  {
139  private:
141  static constexpr bool only_coordinates = traits_t::result_type_rank < with_front_coordinate_type::rank;
142 
144  using score_matrix_t = std::conditional_t<traits_t::is_banded,
145  alignment_score_matrix_one_column_banded<typename traits_t::score_t>,
146  alignment_score_matrix_one_column<typename traits_t::score_t>>;
148  using trace_matrix_t = std::conditional_t<traits_t::is_banded,
149  alignment_trace_matrix_full_banded<typename traits_t::trace_t,
150  only_coordinates>,
151  alignment_trace_matrix_full<typename traits_t::trace_t,
152  only_coordinates>>;
153 
154  public:
156  using type = deferred_crtp_base<alignment_matrix_policy, score_matrix_t, trace_matrix_t>;
157  };
158 
162  template <typename traits_t>
163  struct select_gap_policy
164  {
165  private:
167  using score_t = typename traits_t::score_t;
169  using is_local_t = std::bool_constant<traits_t::is_local>;
170 
171  public:
173  using type = std::conditional_t<traits_t::is_vectorised,
174  deferred_crtp_base<simd_affine_gap_policy, score_t, is_local_t>,
175  deferred_crtp_base<affine_gap_policy, score_t, is_local_t>>;
176  };
177 
184  template <typename traits_t, typename policy_traits_t>
185  struct select_find_optimum_policy
186  {
187  private:
189  using score_t = typename traits_t::score_t;
191  static constexpr bool is_global_alignment = traits_t::is_global && !traits_t::is_aligned_ends;
192 
193  public:
195  using type = std::conditional_t<traits_t::is_vectorised,
196  deferred_crtp_base<simd_find_optimum_policy,
197  score_t,
199  policy_traits_t>,
200  deferred_crtp_base<find_optimum_policy, policy_traits_t>>;
201  };
202 
203 public:
229  template <align_pairwise_range_input sequences_t, typename config_t>
231  requires is_type_specialisation_of_v<config_t, configuration>
233  static constexpr auto configure(config_t const & cfg)
234  {
235 
236  if constexpr (!config_t::template exists<align_cfg::result>())
237  {
238  // Set the default result value to be computed.
239  return configure<sequences_t>(cfg | align_cfg::result{with_score});
240  }
241  else
242  {
243  // ----------------------------------------------------------------------------
244  // Configure the type-erased alignment function.
245  // ----------------------------------------------------------------------------
246 
247  using first_seq_t = std::tuple_element_t<0, value_type_t<sequences_t>>;
248  using second_seq_t = std::tuple_element_t<1, value_type_t<sequences_t>>;
249 
250  using wrapped_first_t = type_reduce_view<first_seq_t &>;
251  using wrapped_second_t = type_reduce_view<second_seq_t &>;
252 
253  // The alignment executor passes a chunk over an indexed sequence pair range to the alignment algorithm.
254  using indexed_sequence_pair_range_t = typename chunked_indexed_sequence_pairs<sequences_t>::type;
255  using indexed_sequence_pair_chunk_t = std::ranges::range_value_t<indexed_sequence_pair_range_t>;
256 
257  // Select the result type based on the sequences and the configuration.
258  using result_t = alignment_result<typename align_result_selector<std::remove_reference_t<wrapped_first_t>,
260  config_t
261  >::type
262  >;
263  using result_collection_t = std::vector<result_t>; // Use a vector as return type.
264  // Define the function wrapper type.
265  using function_wrapper_t = std::function<result_collection_t(indexed_sequence_pair_chunk_t)>;
266 
267  // ----------------------------------------------------------------------------
268  // Test some basic preconditions
269  // ----------------------------------------------------------------------------
270 
271  using alignment_contract_t = alignment_contract<sequences_t, config_t>;
272 
273  static_assert(alignment_contract_t::expects_alignment_configuration(),
274  "Alignment configuration error: "
275  "The alignment can only be configured with alignment configurations.");
276 
277  static_assert(alignment_contract_t::expects_tuple_like_value_type(),
278  "Alignment configuration error: "
279  "The value type of the sequence ranges must model the seqan3::tuple_like "
280  "and must contain exactly 2 elements.");
281 
282  static_assert(alignment_contract_t::expects_valid_scoring_scheme(),
283  "Alignment configuration error: "
284  "Either the scoring scheme was not configured or the given scoring scheme cannot be invoked with "
285  "the value types of the passed sequences.");
286 
287  // ----------------------------------------------------------------------------
288  // Configure the algorithm
289  // ----------------------------------------------------------------------------
290 
291  // Use default edit distance if gaps are not set.
292  auto const & gaps = cfg.template value_or<align_cfg::gap>(gap_scheme{gap_score{-1}});
293  auto const & scoring_scheme = get<align_cfg::scoring>(cfg).value;
294  auto align_ends_cfg = cfg.template value_or<align_cfg::aligned_ends>(free_ends_none);
295 
296  if constexpr (config_t::template exists<align_cfg::mode<detail::global_alignment_type>>())
297  {
298  // Only use edit distance if ...
299  if (gaps.get_gap_open_score() == 0 && // gap open score is not set,
300  !(align_ends_cfg[2] || align_ends_cfg[3]) && // none of the free end gaps are set for second seq,
301  align_ends_cfg[0] == align_ends_cfg[1]) // free ends for leading and trailing gaps are equal in first seq.
302  {
303  // TODO: Instead of relying on nucleotide scoring schemes we need to be able to determine the edit distance
304  // option via the scheme.
305  if constexpr (is_type_specialisation_of_v<remove_cvref_t<decltype(scoring_scheme)>,
306  nucleotide_scoring_scheme>)
307  {
308  if ((scoring_scheme.score('A'_dna15, 'A'_dna15) == 0) &&
309  (scoring_scheme.score('A'_dna15, 'C'_dna15)) == -1)
310  return std::pair{configure_edit_distance<function_wrapper_t>(cfg), cfg};
311  }
312  }
313  }
314 
315  // ----------------------------------------------------------------------------
316  // Check if invalid configuration was used.
317  // ----------------------------------------------------------------------------
318 
319  // Do not allow max error configuration for alignments not computing the edit distance.
320  if (config_t::template exists<align_cfg::max_error>())
321  throw invalid_alignment_configuration{"The align_cfg::max_error configuration is only allowed for "
322  "the specific edit distance computation."};
323  // Configure the alignment algorithm.
324  return std::pair{configure_scoring_scheme<function_wrapper_t>(cfg), cfg};
325  }
326  }
327 
328 private:
334  template <typename function_wrapper_t, typename config_t>
335  static constexpr function_wrapper_t configure_edit_distance(config_t const & cfg)
336  {
337  using traits_t = alignment_configuration_traits<config_t>;
338 
339  // ----------------------------------------------------------------------------
340  // Unsupported configurations
341  // ----------------------------------------------------------------------------
342 
343  if constexpr (traits_t::is_banded)
344  throw invalid_alignment_configuration{"Banded alignments are yet not supported."};
345 
346  // ----------------------------------------------------------------------------
347  // Configure semi-global alignment
348  // ----------------------------------------------------------------------------
349 
350  // Get the value for the sequence ends configuration.
351  auto align_ends_cfg = cfg.template value_or<align_cfg::aligned_ends>(free_ends_none);
352  using align_ends_cfg_t = remove_cvref_t<decltype(align_ends_cfg)>;
353 
354  auto configure_edit_traits = [&] (auto is_semi_global)
355  {
356  struct edit_traits_type
357  {
358  using is_semi_global_type [[maybe_unused]] = remove_cvref_t<decltype(is_semi_global)>;
359  };
360 
361  edit_distance_algorithm<remove_cvref_t<config_t>, edit_traits_type> algorithm{cfg};
362  return function_wrapper_t{std::move(algorithm)};
363  };
364 
365  // Check if it has free ends set for the first sequence trailing gaps.
366  auto has_free_ends_trailing = [&] (auto first) constexpr
367  {
368  if constexpr (!decltype(first)::value)
369  {
370  return configure_edit_traits(std::false_type{});
371  }
372  else if constexpr (align_ends_cfg_t::template is_static<1>())
373  {
374 #if defined(__GNUC__) && __GNUC__ >= 9
375  constexpr bool free_ends_trailing = align_ends_cfg_t::template get_static<1>();
376  return configure_edit_traits(std::integral_constant<bool, free_ends_trailing>{});
377 #else // ^^^ workaround / no workaround vvv
378  return configure_edit_traits(std::integral_constant<bool, align_ends_cfg_t::template get_static<1>()>{});
379 #endif // defined(__GNUC__) && __GNUC__ >= 9
380  }
381  else // Resolve correct property at runtime.
382  {
383  if (align_ends_cfg[1])
384  return configure_edit_traits(std::true_type{});
385  else
386  return configure_edit_traits(std::false_type{});
387  }
388  };
389 
390  // Check if it has free ends set for the first sequence leading gaps.
391  // If possible use static information.
392  if constexpr (align_ends_cfg_t::template is_static<0>())
393  return has_free_ends_trailing(std::integral_constant<bool, align_ends_cfg_t::template get_static<0>()>{});
394  else // Resolve correct property at runtime.
395  {
396  if (align_ends_cfg[0])
397  return has_free_ends_trailing(std::true_type{});
398  else
399  return has_free_ends_trailing(std::false_type{});
400  }
401  }
402 
419  template <typename function_wrapper_t, typename ...policies_t, typename config_t>
420  static constexpr function_wrapper_t configure_free_ends_initialisation(config_t const & cfg);
421 
438  template <typename function_wrapper_t, typename ...policies_t, typename config_t>
439  static constexpr function_wrapper_t configure_free_ends_optimum_search(config_t const & cfg);
440 
457  template <typename function_wrapper_t, typename config_t>
458  static constexpr function_wrapper_t configure_scoring_scheme(config_t const & cfg);
459 
474  template <typename function_wrapper_t, typename ...policies_t, typename config_t>
475  static constexpr function_wrapper_t make_algorithm(config_t const & cfg)
476  {
477  using traits_t = alignment_configuration_traits<config_t>;
478  using matrix_policy_t = typename select_matrix_policy<traits_t>::type;
479  using gap_policy_t = typename select_gap_policy<traits_t>::type;
480 
481  return alignment_algorithm<config_t, matrix_policy_t, gap_policy_t, policies_t...>{cfg};
482  }
483 };
484 
486 template <typename function_wrapper_t, typename config_t>
487 constexpr function_wrapper_t alignment_configurator::configure_scoring_scheme(config_t const & cfg)
488 {
489  using traits_t = alignment_configuration_traits<config_t>;
490 
491  using alignment_scoring_scheme_t =
492  lazy_conditional_t<traits_t::is_vectorised,
493  lazy<simd_match_mismatch_scoring_scheme,
494  typename traits_t::score_t,
495  typename traits_t::scoring_scheme_alphabet_t,
496  typename traits_t::alignment_mode_t>,
497  typename traits_t::scoring_scheme_t>;
498 
499  using scoring_scheme_policy_t = deferred_crtp_base<scoring_scheme_policy, alignment_scoring_scheme_t>;
500  return configure_free_ends_initialisation<function_wrapper_t, scoring_scheme_policy_t>(cfg);
501 }
502 
503 // This function returns a std::function object which can capture runtime dependent alignment algorithm types through
504 // a fixed invocation interface which is already defined by the caller of this function.
505 template <typename function_wrapper_t, typename ...policies_t, typename config_t>
506 constexpr function_wrapper_t alignment_configurator::configure_free_ends_initialisation(config_t const & cfg)
507 {
508  using traits_t = alignment_configuration_traits<config_t>;
509  // Get the value for the sequence ends configuration.
510  auto align_ends_cfg = cfg.template value_or<align_cfg::aligned_ends>(free_ends_none);
511  using align_ends_cfg_t = decltype(align_ends_cfg);
512 
513  // This lambda augments the initialisation policy of the alignment algorithm
514  // with the aligned_ends configuration from before.
515  auto configure_leading_both = [&] (auto first_seq, auto second_seq) constexpr
516  {
517  // Define the trait for the initialisation policy
518  struct policy_trait_type
519  {
520  using free_first_leading_t [[maybe_unused]] = decltype(first_seq);
521  using free_second_leading_t [[maybe_unused]] = decltype(second_seq);
522  };
523 
524  // Make initialisation policy a deferred CRTP base and delegate to configure the find optimum policy.
525  using gap_init_policy_t = deferred_crtp_base<affine_gap_init_policy, policy_trait_type>;
526  return configure_free_ends_optimum_search<function_wrapper_t, policies_t..., gap_init_policy_t>(cfg);
527  };
528 
529  if constexpr (traits_t::is_local)
530  {
531  return configure_leading_both(std::true_type{}, std::true_type{});
532  }
533  else
534  {
535  // This lambda determines the initialisation configuration for the second sequence given
536  // the leading gap property for it.
537  auto configure_leading_second = [&] (auto first) constexpr
538  {
539  // If possible use static information.
540  if constexpr (align_ends_cfg_t::template is_static<2>())
541  {
543  return configure_leading_both(first, second_t{});
544  }
545  else
546  { // Resolve correct property at runtime.
547  if (align_ends_cfg[2])
548  return configure_leading_both(first, std::true_type{});
549  else
550  return configure_leading_both(first, std::false_type{});
551  }
552  };
553 
554  // Here the initialisation configuration for the first sequence is determined given
555  // the leading gap property for it.
556  // If possible use static information.
557  if constexpr (align_ends_cfg_t::template is_static<0>())
558  {
560  return configure_leading_second(first_t{});
561  }
562  else
563  { // Resolve correct property at runtime.
564  if (align_ends_cfg[0])
565  return configure_leading_second(std::true_type{});
566  else
567  return configure_leading_second(std::false_type{});
568  }
569  }
570 }
572 
574 // This function returns a std::function object which can capture runtime dependent alignment algorithm types through
575 // a fixed invocation interface which is already defined by the caller of this function.
576 template <typename function_wrapper_t, typename ...policies_t, typename config_t>
577 constexpr function_wrapper_t alignment_configurator::configure_free_ends_optimum_search(config_t const & cfg)
578 {
579  using traits_t = alignment_configuration_traits<config_t>;
580 
581  // Get the value for the sequence ends configuration.
582  auto align_ends_cfg = cfg.template value_or<align_cfg::aligned_ends>(free_ends_none);
583  using align_ends_cfg_t = decltype(align_ends_cfg);
584 
585  // This lambda augments the find optimum policy of the alignment algorithm with the
586  // respective aligned_ends configuration.
587  auto configure_trailing_both = [&] (auto first_seq, auto second_seq) constexpr
588  {
589  struct policy_trait_type
590  {
591  using find_in_every_cell_type [[maybe_unused]] = std::bool_constant<traits_t::is_local>;
592  using find_in_last_row_type [[maybe_unused]] = decltype(first_seq);
593  using find_in_last_column_type [[maybe_unused]] = decltype(second_seq);
594  };
595 
596  // We need to select the correct policy based on the configuration traits.
597  using find_optimum_t = typename select_find_optimum_policy<traits_t, policy_trait_type>::type;
598  return make_algorithm<function_wrapper_t, policies_t..., find_optimum_t>(cfg);
599  };
600 
601  if constexpr (traits_t::is_local)
602  {
603  return configure_trailing_both(std::true_type{}, std::true_type{});
604  }
605  else
606  {
607  // This lambda determines the lookup configuration for the second sequence given
608  // the trailing gap property for it.
609  auto configure_trailing_second = [&] (auto first) constexpr
610  {
611  // If possible use static information.
612  if constexpr (align_ends_cfg_t::template is_static<3>())
613  {
615  return configure_trailing_both(first, second_t{});
616  }
617  else
618  { // Resolve correct property at runtime.
619  if (align_ends_cfg[3])
620  return configure_trailing_both(first, std::true_type{});
621  else
622  return configure_trailing_both(first, std::false_type{});
623  }
624  };
625 
626  // Here the lookup configuration for the first sequence is determined given
627  // the trailing gap property for it.
628  // If possible use static information.
629  if constexpr (align_ends_cfg_t::template is_static<1>())
630  {
632  return configure_trailing_second(first_t{});
633  }
634  else
635  { // Resolve correct property at runtime.
636  if (align_ends_cfg[1])
637  return configure_trailing_second(std::true_type{});
638  else
639  return configure_trailing_second(std::false_type{});
640  }
641  }
642 }
644 
645 } // namespace seqan3::detail
alignment_trace_matrix_full_banded.hpp
Provides seqan3::detail::alignment_trace_matrix_full_banded.
seqan3::remove_cvref_t
std::remove_cv_t< std::remove_reference_t< t > > remove_cvref_t
Return the input type with const, volatile and references removed (type trait).
Definition: basic.hpp:35
zip.hpp
Provides seqan3::views::zip.
std::bool_constant
utility
type_traits.hpp
Provides helper type traits for the configuration and execution of the alignment algorithm.
edit_distance_algorithm.hpp
Provides seqan3::detail::edit_distance_algorithm.
functional
tuple.hpp
Provides seqan3::tuple_like.
std::pair
vector
seqan3::views::move
const auto move
A view that turns lvalue-references into rvalue-references.
Definition: move.hpp:68
template_inspection.hpp
Provides seqan3::type_list and auxiliary type traits.
tuple
simd_match_mismatch_scoring_scheme.hpp
Provides seqan3::detail::simd_match_mismatch_scoring_scheme.
std::function
scoring_scheme
A concept that requires that type be able to score two letters.
alignment_algorithm.hpp
Provides seqan3::detail::alignment_algorithm.
simd.hpp
Provides seqan3::simd::simd_type.
alignment_score_matrix_one_column.hpp
Provides seqan3::detail::alignment_score_matrix_one_column.
type_reduce.hpp
Provides seqan3::views::type_reduce.
concept.hpp
Provides concepts needed internally for the alignment algorithms.
alignment_result.hpp
Provides seqan3::alignment_result.
all.hpp
Meta-header for the alignment configuration module .
tuple_like
Whether a type behaves like a tuple.
std::remove_reference_t
std
SeqAn specific customisations in the standard namespace.
std::conditional_t
deferred_crtp_base.hpp
Provides seqan3::detail::deferred_crtp_base.
scoring_scheme::score
score_type score(alph1_t const alph1, alph2_t const alph2)
Compute the score of two letters.
align_result_selector.hpp
Provides seqan3::detail::align_result_selector.
lazy.hpp
Provides lazy template instantiation traits.
alignment_score_matrix_one_column_banded.hpp
Provides seqan3::detail::alignment_score_matrix_one_column_banded.
alignment_trace_matrix_full.hpp
Provides seqan3::detail::alignment_trace_matrix_full.
type_list.hpp
Provides seqan3::type_list.