SeqAn3  3.0.0
The Modern C++ library for sequence analysis.
alignment_configurator.hpp
Go to the documentation of this file.
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 
13 #pragma once
14 
15 #include <functional>
16 #include <tuple>
17 #include <utility>
18 #include <vector>
19 
21 #include <seqan3/alignment/pairwise/policy/all.hpp>
32 
33 namespace seqan3::detail
34 {
37 template <typename value_t>
38 SEQAN3_CONCEPT AlignPairwiseValue =
39  TupleLike<value_t> &&
40  std::tuple_size_v<value_t> == 2 &&
43 
48 template <typename value_t>
49 SEQAN3_CONCEPT AlignPairwiseSingleInput =
50  AlignPairwiseValue<value_t> &&
53 
68 template <typename range_t>
69 SEQAN3_CONCEPT AlignPairwiseRangeInputConcept =
71  AlignPairwiseValue<value_type_t<range_t>> &&
72  ((std::ranges::ViewableRange<range_t> && std::is_lvalue_reference_v<reference_t<range_t>>) ||
73  AlignPairwiseSingleInput<std::remove_reference_t<reference_t<range_t>>>);
74 
84 template <typename range_type,
85  typename alignment_config_type>
86 struct alignment_contract
87 {
88 private:
92  using unref_range_type = std::remove_reference_t<range_type>;
95  using first_seq_t = std::tuple_element_t<0, value_type_t<std::ranges::iterator_t<unref_range_type>>>;
97  using second_seq_t = std::tuple_element_t<1, value_type_t<std::ranges::iterator_t<unref_range_type>>>;
99 
100 public:
102  constexpr static bool expects_tuple_like_value_type()
103  {
104  return TupleLike<alignment_config_type> &&
105  std::tuple_size_v<value_type_t<std::ranges::iterator_t<unref_range_type>>> == 2;
106  }
107 
109  constexpr static bool expects_valid_scoring_scheme()
110  {
111  if constexpr (alignment_config_type::template exists<align_cfg::scoring>())
112  {
113  using scoring_type = std::remove_reference_t<
114  decltype(get<align_cfg::scoring>(std::declval<alignment_config_type>()).value)
115  >;
116  return static_cast<bool>(ScoringScheme<scoring_type,
117  value_type_t<first_seq_t>,
118  value_type_t<second_seq_t>>);
119  }
120  else
121  {
122  return false;
123  }
124  }
125 
127  constexpr static bool expects_alignment_configuration()
128  {
129  return alignment_config_type::template exists<align_cfg::mode>();
130  }
131 };
132 
137 struct alignment_configurator
138 {
139 private:
140 
145  template <typename config_type, typename score_allocator_t, typename trace_allocator_t>
146  struct select_matrix_policy
147  {
148  private:
149 
151  template <typename config_t>
152  static constexpr auto select() noexcept
153  {
154  // Check whether traceback was requested or not.
155  if constexpr (std::is_same_v<typename trace_allocator_t::value_type, ignore_t>)
156  { // No traceback
157  if constexpr (config_t::template exists<align_cfg::band>())
158  return deferred_crtp_base<banded_score_dp_matrix_policy, score_allocator_t>{};
159  else
160  return deferred_crtp_base<unbanded_score_dp_matrix_policy, score_allocator_t>{};
161  }
162  else
163  { // requested traceback
164  if constexpr (config_t::template exists<align_cfg::band>())
165  {
166  return deferred_crtp_base<banded_score_trace_dp_matrix_policy,
167  score_allocator_t,
168  trace_allocator_t>{};
169  }
170  else
171  {
172  return deferred_crtp_base<unbanded_score_trace_dp_matrix_policy,
173  score_allocator_t,
174  trace_allocator_t>{};
175  }
176  }
177  }
178 
179  public:
181  using type = decltype(select<config_type>());
182  };
183 
188  template <typename config_type, typename ... trait_types>
189  struct select_gap_policy
190  {
191  private:
192 
194  template <typename config_t>
195  static constexpr auto select() noexcept
196  {
197  if constexpr (config_t::template exists<align_cfg::band>())
198  return deferred_crtp_base<affine_gap_banded_policy, trait_types...>{};
199  else
200  return deferred_crtp_base<affine_gap_policy, trait_types...>{};
201  }
202 
203  public:
205  using type = decltype(select<config_type>());
206  };
207 
212  template <typename config_type, typename ... trait_types>
213  struct select_gap_init_policy
214  {
215  private:
216 
218  template <typename config_t>
219  static constexpr auto select() noexcept
220  {
221  if constexpr (config_t::template exists<align_cfg::band>())
222  return deferred_crtp_base<affine_gap_banded_init_policy, trait_types...>{};
223  else
224  return deferred_crtp_base<affine_gap_init_policy, trait_types...>{};
225  }
226 
227  public:
229  using type = decltype(select<config_type>());
230  };
231 
232 public:
252  template <AlignPairwiseRangeInputConcept sequences_t, typename config_t>
254  requires is_type_specialisation_of_v<config_t, configuration>
256  static constexpr auto configure(config_t const & cfg)
257  {
258 
259  if constexpr (!config_t::template exists<align_cfg::result>())
260  {
261  // ----------------------------------------------------------------------------
262  // Set the default result value to be computed.
263  // ----------------------------------------------------------------------------
264 
265  return configure<sequences_t>(cfg | align_cfg::result{with_score});
266  }
267  else
268  {
269  // ----------------------------------------------------------------------------
270  // Configure the type-erased alignment function.
271  // ----------------------------------------------------------------------------
272 
273  using first_seq_t = std::tuple_element_t<0, value_type_t<sequences_t>>;
274  using second_seq_t = std::tuple_element_t<1, value_type_t<sequences_t>>;
275 
276  using wrapped_first_t = all_view<first_seq_t &>;
277  using wrapped_second_t = all_view<second_seq_t &>;
278 
279  // Select the result type based on the sequences and the configuration.
280  using result_t = alignment_result<typename align_result_selector<std::remove_reference_t<wrapped_first_t>,
282  config_t
283  >::type
284  >;
285  // Define the function wrapper type.
287 
288  // ----------------------------------------------------------------------------
289  // Test some basic preconditions
290  // ----------------------------------------------------------------------------
291 
292  using alignment_contract_t = alignment_contract<sequences_t, config_t>;
293 
294  static_assert(alignment_contract_t::expects_alignment_configuration(),
295  "Alignment configuration error: "
296  "The alignment can only be configured with alignment configurations.");
297 
298  static_assert(alignment_contract_t::expects_tuple_like_value_type(),
299  "Alignment configuration error: "
300  "The value type of the sequence ranges must model the seqan3::TupleLike "
301  "and must contain exactly 2 elements.");
302 
303  static_assert(alignment_contract_t::expects_valid_scoring_scheme(),
304  "Alignment configuration error: "
305  "Either the scoring scheme was not configured or the given scoring scheme cannot be invoked with "
306  "the value types of the passed sequences.");
307 
308  // ----------------------------------------------------------------------------
309  // Configure the algorithm
310  // ----------------------------------------------------------------------------
311 
312  // Use default edit distance if gaps are not set.
313  auto const & gaps = cfg.template value_or<align_cfg::gap>(gap_scheme{gap_score{-1}});
314  auto const & scoring_scheme = get<align_cfg::scoring>(cfg).value;
315  auto align_ends_cfg = cfg.template value_or<align_cfg::aligned_ends>(free_ends_none);
316 
317  if constexpr (config_t::template exists<align_cfg::mode<detail::global_alignment_type>>())
318  {
319  // Only use edit distance if ...
320  if (gaps.get_gap_open_score() == 0 && // gap open score is not set,
321  !(align_ends_cfg[2] || align_ends_cfg[3]) && // none of the free end gaps are set for second seq,
322  align_ends_cfg[0] == align_ends_cfg[1]) // free ends for leading and trailing gaps are equal in first seq.
323  {
324  // TODO: Instead of relying on nucleotide scoring schemes we need to be able to determine the edit distance
325  // option via the scheme.
326  if constexpr (is_type_specialisation_of_v<remove_cvref_t<decltype(scoring_scheme)>,
327  nucleotide_scoring_scheme>)
328  {
329  if ((scoring_scheme.score('A'_dna15, 'A'_dna15) == 0) &&
330  (scoring_scheme.score('A'_dna15, 'C'_dna15)) == -1)
331  return configure_edit_distance<function_wrapper_t>(cfg);
332  }
333  }
334  }
335 
336  // ----------------------------------------------------------------------------
337  // Check if invalid configuration was used.
338  // ----------------------------------------------------------------------------
339 
340  // Do not allow max error configuration for alignments not computing the edit distance.
341  if (config_t::template exists<align_cfg::max_error>())
342  throw invalid_alignment_configuration{"The align_cfg::max_error configuration is only allowed for "
343  "the specific edit distance computation."};
344  // Configure the alignment algorithm.
345  return configure_free_ends_initialisation<function_wrapper_t>(cfg);
346  }
347  }
348 
349 private:
350 
356  template <typename function_wrapper_t, typename config_t>
357  static constexpr function_wrapper_t configure_edit_distance(config_t const & cfg)
358  {
359  // ----------------------------------------------------------------------------
360  // Unsupported configurations
361  // ----------------------------------------------------------------------------
362 
363  if constexpr (config_t::template exists<align_cfg::band>())
364  throw invalid_alignment_configuration{"Banded alignments are yet not supported."};
365 
366  // ----------------------------------------------------------------------------
367  // Configure semi-global alignment
368  // ----------------------------------------------------------------------------
369 
370  // Get the value for the sequence ends configuration.
371  auto align_ends_cfg = cfg.template value_or<align_cfg::aligned_ends>(free_ends_none);
372  using align_ends_cfg_t = remove_cvref_t<decltype(align_ends_cfg)>;
373 
374  auto configure_edit_traits = [&] (auto is_semi_global)
375  {
376  struct edit_traits_type
377  {
378  using is_semi_global_type [[maybe_unused]] = remove_cvref_t<decltype(is_semi_global)>;
379  };
380 
381  edit_distance_algorithm<remove_cvref_t<config_t>, edit_traits_type> algorithm{cfg};
382  return function_wrapper_t{std::move(algorithm)};
383  };
384 
385  // Check if it has free ends set for the first sequence trailing gaps.
386  auto has_free_ends_trailing = [&] (auto first) constexpr
387  {
388  if constexpr (!decltype(first)::value)
389  {
390  return configure_edit_traits(std::false_type{});
391  }
392  else if constexpr (align_ends_cfg_t::template is_static<1>())
393  {
394 #if defined(__GNUC__) && __GNUC__ >= 9
395  constexpr bool free_ends_trailing = align_ends_cfg_t::template get_static<1>();
396  return configure_edit_traits(std::integral_constant<bool, free_ends_trailing>{});
397 #else // ^^^ workaround / no workaround vvv
398  return configure_edit_traits(std::integral_constant<bool, align_ends_cfg_t::template get_static<1>()>{});
399 #endif // defined(__GNUC__) && __GNUC__ >= 9
400  }
401  else // Resolve correct property at runtime.
402  {
403  if (align_ends_cfg[1])
404  return configure_edit_traits(std::true_type{});
405  else
406  return configure_edit_traits(std::false_type{});
407  }
408  };
409 
410  // Check if it has free ends set for the first sequence leading gaps.
411  // If possible use static information.
412  if constexpr (align_ends_cfg_t::template is_static<0>())
413  return has_free_ends_trailing(std::integral_constant<bool, align_ends_cfg_t::template get_static<0>()>{});
414  else // Resolve correct property at runtime.
415  {
416  if (align_ends_cfg[0])
417  return has_free_ends_trailing(std::true_type{});
418  else
419  return has_free_ends_trailing(std::false_type{});
420  }
421  }
422 
434  template <typename function_wrapper_t, typename config_t>
435  static constexpr function_wrapper_t configure_free_ends_initialisation(config_t const & cfg);
436 
449  template <typename function_wrapper_t, typename ...policies_t, typename config_t>
450  static constexpr function_wrapper_t configure_free_ends_optimum_search(config_t const & cfg);
451 
455  template <typename config_t>
456  struct configure_trace_type
457  {
461  config_t::template exists<align_cfg::result<with_front_coordinate_type>>(),
462  trace_directions,
463  ignore_t>;
464  };
465 
466 };
467 
469 // This function returns a std::function object which can capture runtime dependent alignment algorithm types through
470 // a fixed invocation interface which is already defined by the caller of this function.
471 template <typename function_wrapper_t, typename config_t>
472 constexpr function_wrapper_t alignment_configurator::configure_free_ends_initialisation(config_t const & cfg)
473 {
474  // ----------------------------------------------------------------------------
475  // score and cell type
476  // ----------------------------------------------------------------------------
477 
478  using score_type = int32_t;
479  using trace_type = typename configure_trace_type<config_t>::type;
481 
482  // ----------------------------------------------------------------------------
483  // dynamic programming matrix
484  // ----------------------------------------------------------------------------
485 
486  using dp_matrix_t = typename select_matrix_policy<config_t,
489 
490  // ----------------------------------------------------------------------------
491  // affine gap kernel
492  // ----------------------------------------------------------------------------
493 
495  using affine_t = typename select_gap_policy<config_t, cell_type, local_t>::type;
496 
497  // ----------------------------------------------------------------------------
498  // configure initialisation policy
499  // ----------------------------------------------------------------------------
500 
501  // Get the value for the sequence ends configuration.
502  auto align_ends_cfg = cfg.template value_or<align_cfg::aligned_ends>(free_ends_none);
503  using align_ends_cfg_t = decltype(align_ends_cfg);
504 
505  // This lambda augments the initialisation policy of the alignment algorithm
506  // with the aligned_ends configuration from before.
507  auto configure_leading_both = [&] (auto first_seq, auto second_seq) constexpr
508  {
509  // Define the trait for the initialisation policy
510  struct policy_trait_type
511  {
512  using free_first_leading_t [[maybe_unused]] = decltype(first_seq);
513  using free_second_leading_t [[maybe_unused]] = decltype(second_seq);
514  };
515 
516  // Make initialisation policy a deferred CRTP base and delegate to configure the find optimum policy.
517  using init_t = typename select_gap_init_policy<config_t, policy_trait_type>::type;
518  return configure_free_ends_optimum_search<function_wrapper_t, affine_t, dp_matrix_t, init_t>(cfg);
519  };
520 
521  if constexpr (local_t::value)
522  {
523  return configure_leading_both(std::true_type{}, std::true_type{});
524  }
525  else
526  {
527  // This lambda determines the initialisation configuration for the second sequence given
528  // the leading gap property for it.
529  auto configure_leading_second = [&] (auto first) constexpr
530  {
531  // If possible use static information.
532  if constexpr (align_ends_cfg_t::template is_static<2>())
533  {
535  return configure_leading_both(first, second_t{});
536  }
537  else
538  { // Resolve correct property at runtime.
539  if (align_ends_cfg[2])
540  return configure_leading_both(first, std::true_type{});
541  else
542  return configure_leading_both(first, std::false_type{});
543  }
544  };
545 
546  // Here the initialisation configuration for the first sequence is determined given
547  // the leading gap property for it.
548  // If possible use static information.
549  if constexpr (align_ends_cfg_t::template is_static<0>())
550  {
552  return configure_leading_second(first_t{});
553  }
554  else
555  { // Resolve correct property at runtime.
556  if (align_ends_cfg[0])
557  return configure_leading_second(std::true_type{});
558  else
559  return configure_leading_second(std::false_type{});
560  }
561  }
562 }
564 
566 // This function returns a std::function object which can capture runtime dependent alignment algorithm types through
567 // a fixed invocation interface which is already defined by the caller of this function.
568 template <typename function_wrapper_t, typename ...policies_t, typename config_t>
569 constexpr function_wrapper_t alignment_configurator::configure_free_ends_optimum_search(config_t const & cfg)
570 {
571  // Get the value for the sequence ends configuration.
572  auto align_ends_cfg = cfg.template value_or<align_cfg::aligned_ends>(free_ends_none);
573  using align_ends_cfg_t = decltype(align_ends_cfg);
574 
576 
577  // This lambda augments the find optimum policy of the alignment algorithm with the
578  // respective aligned_ends configuration.
579  auto configure_trailing_both = [&] (auto first_seq, auto second_seq) constexpr
580  {
581  struct policy_trait_type
582  {
583  using find_in_every_cell_type [[maybe_unused]] = local_t;
584  using find_in_last_row_type [[maybe_unused]] = decltype(first_seq);
585  using find_in_last_column_type [[maybe_unused]] = decltype(second_seq);
586  };
587 
588  using find_optimum_t = deferred_crtp_base<find_optimum_policy, policy_trait_type>;
589  return function_wrapper_t{alignment_algorithm<config_t, policies_t..., find_optimum_t>{cfg}};
590  };
591 
592  if constexpr (local_t::value)
593  {
594  return configure_trailing_both(std::true_type{}, std::true_type{});
595  }
596  else
597  {
598  // This lambda determines the lookup configuration for the second sequence given
599  // the trailing gap property for it.
600  auto configure_trailing_second = [&] (auto first) constexpr
601  {
602  // If possible use static information.
603  if constexpr (align_ends_cfg_t::template is_static<3>())
604  {
606  return configure_trailing_both(first, second_t{});
607  }
608  else
609  { // Resolve correct property at runtime.
610  if (align_ends_cfg[3])
611  return configure_trailing_both(first, std::true_type{});
612  else
613  return configure_trailing_both(first, std::false_type{});
614  }
615  };
616 
617  // Here the lookup configuration for the first sequence is determined given
618  // the trailing gap property for it.
619  // If possible use static information.
620  if constexpr (align_ends_cfg_t::template is_static<1>())
621  {
623  return configure_trailing_second(first_t{});
624  }
625  else
626  { // Resolve correct property at runtime.
627  if (align_ends_cfg[1])
628  return configure_trailing_second(std::true_type{});
629  else
630  return configure_trailing_second(std::false_type{});
631  }
632  }
633 }
635 } // namespace seqan3::detail
Provides seqan3::detail::alignment_algorithm.
Specifies the requirements of a Range type that is either a std::ranges::View or an lvalue-reference...
Meta-header for the alignment configuration module .
Provides seqan3::type_list and auxiliary type traits.
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
SeqAn specific customisations in the standard namespace.
Provides seqan3::detail::deferred_crtp_base.
Provides seqan3::type_list and auxiliary type traits.
Provides seqan3::TupleLike.
Provides seqan3::alignment_result.
Provides seqan3::view::all.
Definition: aligned_sequence_concept.hpp:35
Provides seqan3::detail::align_result_selector.
Specifies requirements of a Range type for which begin returns a type that models std::InputIterator...
Provides various transformation traits used by the range module.
Specifies requirements of a Range type for which begin returns a type that models std::ForwardIterato...
Provides seqan3::detail::edit_distance_algorithm.