SeqAn3  3.0.2
The Modern C++ library for sequence analysis.
simd_find_optimum_policy.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 <type_traits>
16 
23 
24 namespace seqan3::detail
25 {
26 
38 template <simd::simd_concept simd_t>
39 struct simd_global_alignment_state
40 {
42  simd_t score_offset{};
44  simd_t coordinate_offset{};
46  simd_t last_column_mask{};
48  simd_t last_row_mask{};
49 };
50 
77 template <typename alignment_algorithm_t, simd::simd_concept simd_t>
78 class simd_find_optimum_policy : public simd_global_alignment_state<simd_t>
79 {
80 private:
82  friend alignment_algorithm_t;
83 
85  bool is_global_alignment{false};
87  bool test_every_cell{false};
89  bool test_last_row_cell{false};
91  bool test_last_column_cell{false};
92 
96  constexpr simd_find_optimum_policy() = default;
97  constexpr simd_find_optimum_policy(simd_find_optimum_policy const &) = default;
98  constexpr simd_find_optimum_policy(simd_find_optimum_policy &&) = default;
99  constexpr simd_find_optimum_policy & operator=(simd_find_optimum_policy const &) = default;
100  constexpr simd_find_optimum_policy & operator=(simd_find_optimum_policy &&) = default;
101  ~simd_find_optimum_policy() = default;
102 
104  template <typename configuration_t>
105  simd_find_optimum_policy(configuration_t const & config)
106  {
107  if constexpr (configuration_t::template exists<align_cfg::method_local>())
108  test_every_cell = true;
109 
110  is_global_alignment = configuration_t::template exists<align_cfg::method_global>();
111 
112  auto method_global_config = config.get_or(align_cfg::method_global{});
113 
114  test_last_row_cell = method_global_config.free_end_gaps_sequence1_trailing || is_global_alignment;
115  test_last_column_cell = method_global_config.free_end_gaps_sequence2_trailing || is_global_alignment;
116  }
118 
119 protected:
121  template <typename cell_t, typename score_t>
122  constexpr void check_score_of_cell([[maybe_unused]] cell_t const & current_cell,
123  [[maybe_unused]] alignment_algorithm_state<score_t> & state) const noexcept
124  {
125  if (test_every_cell)
126  check_and_update(current_cell, state);
127  }
128 
130  template <typename other_alignment_algorithm_t, simd_concept score_t, typename is_local_t>
131  friend class simd_affine_gap_policy;
132 
134  template <typename other_alignment_algorithm_t>
135  friend class affine_gap_init_policy;
136 
138  template <typename cell_t, typename score_t>
139  constexpr void check_score_of_last_row_cell([[maybe_unused]] cell_t const & last_row_cell,
140  [[maybe_unused]] alignment_algorithm_state<score_t> & state) const
141  noexcept
142  {
143  // Only search in last row if requested and not done already.}
144  if (!test_every_cell && test_last_row_cell)
145  {
146  if (is_global_alignment)
147  check_and_update<true>(last_row_cell, state);
148  else
149  check_and_update(last_row_cell, state);
150  }
151  }
152 
154  template <typename alignment_column_t, typename score_t>
155  constexpr void check_score_of_cells_in_last_column([[maybe_unused]] alignment_column_t && last_column,
156  [[maybe_unused]] alignment_algorithm_state<score_t> & state)
157  const noexcept
158  {
159  // Only check last cell if not done before.
160  if (!test_every_cell && test_last_column_cell)
161  {
162  for (auto && cell : last_column)
163  {
164  if (is_global_alignment)
165  check_and_update<false>(cell, state);
166  else
167  check_and_update(cell, state);
168  }
169  }
170  }
171 
173  template <typename cell_t, typename score_t>
174  constexpr void check_score_of_last_cell([[maybe_unused]] cell_t const & last_cell,
175  [[maybe_unused]] alignment_algorithm_state<score_t> & state) const noexcept
176  {
177  // Only check last cell if not done before.
178  if (!(test_every_cell || test_last_row_cell || test_last_column_cell))
179  check_and_update(last_cell, state);
180  }
181 
194  template <std::ranges::forward_range sequence1_collection_t,
195  std::ranges::forward_range sequence2_collection_t,
196  arithmetic score_t>
197  void initialise_find_optimum_policy([[maybe_unused]] sequence1_collection_t && sequence1_collection,
198  [[maybe_unused]] sequence2_collection_t && sequence2_collection,
199  [[maybe_unused]] score_t const padding_score)
200  {
201  if (is_global_alignment)
202  {
203  assert(std::ranges::distance(sequence1_collection) == std::ranges::distance(sequence2_collection));
204 
205  constexpr size_t simd_size = simd_traits<simd_t>::length;
206  // First get global size.
207  std::array<size_t, simd_size> sequence1_sizes{};
208  std::array<size_t, simd_size> sequence2_sizes{};
209 
210  std::ptrdiff_t max_sequence1_size{};
211  std::ptrdiff_t max_sequence2_size{};
212 
213  size_t array_index{};
214  for (auto && [sequence1, sequence2] : views::zip(sequence1_collection, sequence2_collection))
215  {
216  sequence1_sizes[array_index] = std::ranges::distance(sequence1);
217  sequence2_sizes[array_index] = std::ranges::distance(sequence2);
218  max_sequence1_size = std::max<std::ptrdiff_t>(sequence1_sizes[array_index], max_sequence1_size);
219  max_sequence2_size = std::max<std::ptrdiff_t>(sequence2_sizes[array_index], max_sequence2_size);
220  ++array_index;
221  }
222 
223  // The global diagonal ending in the sink of the outer alignment matrix.
224  std::ptrdiff_t global_diagonal = max_sequence1_size - max_sequence2_size;
225 
226  for (size_t simd_index = 0; simd_index < simd_size; ++simd_index)
227  {
228  if (std::ptrdiff_t local_diagonal = sequence1_sizes[simd_index] - sequence2_sizes[simd_index];
229  local_diagonal < global_diagonal)
230  { // optimum is stored in last row.
231  this->last_row_mask[simd_index] = max_sequence1_size - (global_diagonal - local_diagonal);
232  this->last_column_mask[simd_index] = max_sequence2_size + 1;
233  this->coordinate_offset[simd_index] = max_sequence2_size - sequence2_sizes[simd_index];
234  this->score_offset[simd_index] = padding_score * this->coordinate_offset[simd_index];
235  }
236  else // optimum is stored in last column
237  {
238  this->last_column_mask[simd_index] = max_sequence2_size - (local_diagonal - global_diagonal);
239  this->last_row_mask[simd_index] = max_sequence1_size + 1;
240  this->coordinate_offset[simd_index] = max_sequence1_size - sequence1_sizes[simd_index];
241  this->score_offset[simd_index] = padding_score * this->coordinate_offset[simd_index];
242  }
243  }
244  }
245  // else no-op
246  }
247 
248 private:
257  template <typename cell_t, typename score_t>
258  constexpr void check_and_update(cell_t const & cell, alignment_algorithm_state<score_t> & state) const noexcept
259  {
260  assert(!is_global_alignment); // This function should not be called for the global alignment.
261 
262  auto const & [score_cell, trace_cell] = cell;
263  state.optimum.update_if_new_optimal_score(score_cell.current,
264  column_index_type{trace_cell.coordinate.first},
265  row_index_type{trace_cell.coordinate.second});
266  }
267 
284  template <bool in_last_row, typename cell_t, typename score_t>
285  constexpr void check_and_update(cell_t const & cell, alignment_algorithm_state<score_t> & state) const noexcept
286  {
287  assert(is_global_alignment); // This function should only be called for the global alignment.
288 
289  using simd_mask_t = typename simd_traits<simd_t>::mask_type;
290  auto const & [score_cell, trace_cell] = cell;
291  simd_t column_positions = simd::fill<simd_t>(trace_cell.coordinate.first);
292  simd_t row_positions = simd::fill<simd_t>(trace_cell.coordinate.second);
293 
294  simd_mask_t mask{};
295 
296  if constexpr (in_last_row) // Check if column was masked
297  mask = (column_positions == this->last_row_mask);
298  else // Check if row was masked
299  mask = (row_positions == this->last_column_mask);
300 
301  // In global alignment we are only interested in the position not the max of the scores.
302  // In addition, the scores need to be corrected in order to track the right score.
303  state.optimum.score = mask ? score_cell.current - this->score_offset : state.optimum.score;
304  state.optimum.column_index = mask ? column_positions - this->coordinate_offset
305  : state.optimum.column_index;
306  state.optimum.row_index = mask ? row_positions - this->coordinate_offset
307  : state.optimum.row_index;
308  }
309 };
310 
311 } // namespace seqan3::detail
zip.hpp
Provides seqan3::views::zip.
concept.hpp
Provides seqan3::simd::simd_concept.
std::array
alignment_optimum.hpp
Provides seqan3::detail::alignment_optimum.
empty_type.hpp
Provides seqan3::detail::empty_type.
find_optimum_policy.hpp
Provides seqan3::detail::find_optimum_policy.
alignment_algorithm_state.hpp
Provides seqan3::detail::alignment_algorithm_state.
std::ptrdiff_t
arithmetic
A type that satisfies std::is_arithmetic_v<t>.