stride-align 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/NOTICE +255 -0
- data/README.md +107 -0
- data/data/bmpm_data/gen_approx_any.txt +131 -0
- data/data/bmpm_data/gen_approx_arabic.txt +26 -0
- data/data/bmpm_data/gen_approx_common.txt +233 -0
- data/data/bmpm_data/gen_approx_cyrillic.txt +18 -0
- data/data/bmpm_data/gen_approx_czech.txt +18 -0
- data/data/bmpm_data/gen_approx_dutch.txt +18 -0
- data/data/bmpm_data/gen_approx_english.txt +47 -0
- data/data/bmpm_data/gen_approx_french.txt +25 -0
- data/data/bmpm_data/gen_approx_german.txt +73 -0
- data/data/bmpm_data/gen_approx_greek.txt +18 -0
- data/data/bmpm_data/gen_approx_greeklatin.txt +20 -0
- data/data/bmpm_data/gen_approx_hebrew.txt +18 -0
- data/data/bmpm_data/gen_approx_hungarian.txt +18 -0
- data/data/bmpm_data/gen_approx_italian.txt +18 -0
- data/data/bmpm_data/gen_approx_polish.txt +84 -0
- data/data/bmpm_data/gen_approx_portuguese.txt +18 -0
- data/data/bmpm_data/gen_approx_romanian.txt +18 -0
- data/data/bmpm_data/gen_approx_russian.txt +48 -0
- data/data/bmpm_data/gen_approx_spanish.txt +21 -0
- data/data/bmpm_data/gen_approx_turkish.txt +18 -0
- data/data/bmpm_data/gen_exact_any.txt +40 -0
- data/data/bmpm_data/gen_exact_approx_common.txt +79 -0
- data/data/bmpm_data/gen_exact_arabic.txt +18 -0
- data/data/bmpm_data/gen_exact_common.txt +32 -0
- data/data/bmpm_data/gen_exact_cyrillic.txt +18 -0
- data/data/bmpm_data/gen_exact_czech.txt +18 -0
- data/data/bmpm_data/gen_exact_dutch.txt +18 -0
- data/data/bmpm_data/gen_exact_english.txt +18 -0
- data/data/bmpm_data/gen_exact_french.txt +18 -0
- data/data/bmpm_data/gen_exact_german.txt +18 -0
- data/data/bmpm_data/gen_exact_greek.txt +18 -0
- data/data/bmpm_data/gen_exact_greeklatin.txt +18 -0
- data/data/bmpm_data/gen_exact_hebrew.txt +18 -0
- data/data/bmpm_data/gen_exact_hungarian.txt +18 -0
- data/data/bmpm_data/gen_exact_italian.txt +18 -0
- data/data/bmpm_data/gen_exact_polish.txt +23 -0
- data/data/bmpm_data/gen_exact_portuguese.txt +18 -0
- data/data/bmpm_data/gen_exact_romanian.txt +18 -0
- data/data/bmpm_data/gen_exact_russian.txt +19 -0
- data/data/bmpm_data/gen_exact_spanish.txt +19 -0
- data/data/bmpm_data/gen_exact_turkish.txt +18 -0
- data/data/bmpm_data/gen_hebrew_common.txt +113 -0
- data/data/bmpm_data/gen_lang.txt +295 -0
- data/data/bmpm_data/gen_languages.txt +36 -0
- data/data/bmpm_data/gen_rules_any.txt +367 -0
- data/data/bmpm_data/gen_rules_arabic.txt +76 -0
- data/data/bmpm_data/gen_rules_cyrillic.txt +99 -0
- data/data/bmpm_data/gen_rules_czech.txt +67 -0
- data/data/bmpm_data/gen_rules_dutch.txt +78 -0
- data/data/bmpm_data/gen_rules_english.txt +113 -0
- data/data/bmpm_data/gen_rules_french.txt +114 -0
- data/data/bmpm_data/gen_rules_german.txt +129 -0
- data/data/bmpm_data/gen_rules_greek.txt +97 -0
- data/data/bmpm_data/gen_rules_greeklatin.txt +118 -0
- data/data/bmpm_data/gen_rules_hebrew.txt +62 -0
- data/data/bmpm_data/gen_rules_hungarian.txt +83 -0
- data/data/bmpm_data/gen_rules_italian.txt +77 -0
- data/data/bmpm_data/gen_rules_polish.txt +185 -0
- data/data/bmpm_data/gen_rules_portuguese.txt +105 -0
- data/data/bmpm_data/gen_rules_romanian.txt +64 -0
- data/data/bmpm_data/gen_rules_russian.txt +142 -0
- data/data/bmpm_data/gen_rules_spanish.txt +85 -0
- data/data/bmpm_data/gen_rules_turkish.txt +50 -0
- data/data/keyboard_data/qwerty.npy +0 -0
- data/data/matrix_data/BLOSUM100 +31 -0
- data/data/matrix_data/BLOSUM30 +31 -0
- data/data/matrix_data/BLOSUM35 +31 -0
- data/data/matrix_data/BLOSUM40 +31 -0
- data/data/matrix_data/BLOSUM45 +25 -0
- data/data/matrix_data/BLOSUM50 +25 -0
- data/data/matrix_data/BLOSUM55 +31 -0
- data/data/matrix_data/BLOSUM60 +31 -0
- data/data/matrix_data/BLOSUM62 +25 -0
- data/data/matrix_data/BLOSUM65 +31 -0
- data/data/matrix_data/BLOSUM70 +31 -0
- data/data/matrix_data/BLOSUM75 +31 -0
- data/data/matrix_data/BLOSUM80 +25 -0
- data/data/matrix_data/BLOSUM85 +31 -0
- data/data/matrix_data/BLOSUM90 +25 -0
- data/data/matrix_data/NUC.4.4 +25 -0
- data/data/matrix_data/PAM10 +34 -0
- data/data/matrix_data/PAM100 +34 -0
- data/data/matrix_data/PAM110 +34 -0
- data/data/matrix_data/PAM120 +34 -0
- data/data/matrix_data/PAM130 +34 -0
- data/data/matrix_data/PAM140 +34 -0
- data/data/matrix_data/PAM150 +34 -0
- data/data/matrix_data/PAM160 +34 -0
- data/data/matrix_data/PAM170 +34 -0
- data/data/matrix_data/PAM180 +34 -0
- data/data/matrix_data/PAM190 +34 -0
- data/data/matrix_data/PAM20 +34 -0
- data/data/matrix_data/PAM200 +34 -0
- data/data/matrix_data/PAM210 +34 -0
- data/data/matrix_data/PAM220 +34 -0
- data/data/matrix_data/PAM230 +34 -0
- data/data/matrix_data/PAM240 +34 -0
- data/data/matrix_data/PAM250 +25 -0
- data/data/matrix_data/PAM260 +34 -0
- data/data/matrix_data/PAM270 +34 -0
- data/data/matrix_data/PAM280 +34 -0
- data/data/matrix_data/PAM290 +34 -0
- data/data/matrix_data/PAM30 +25 -0
- data/data/matrix_data/PAM300 +34 -0
- data/data/matrix_data/PAM310 +34 -0
- data/data/matrix_data/PAM320 +34 -0
- data/data/matrix_data/PAM330 +34 -0
- data/data/matrix_data/PAM340 +34 -0
- data/data/matrix_data/PAM350 +34 -0
- data/data/matrix_data/PAM360 +34 -0
- data/data/matrix_data/PAM370 +34 -0
- data/data/matrix_data/PAM380 +34 -0
- data/data/matrix_data/PAM390 +34 -0
- data/data/matrix_data/PAM40 +34 -0
- data/data/matrix_data/PAM400 +34 -0
- data/data/matrix_data/PAM410 +34 -0
- data/data/matrix_data/PAM420 +34 -0
- data/data/matrix_data/PAM430 +34 -0
- data/data/matrix_data/PAM440 +34 -0
- data/data/matrix_data/PAM450 +34 -0
- data/data/matrix_data/PAM460 +34 -0
- data/data/matrix_data/PAM470 +34 -0
- data/data/matrix_data/PAM480 +34 -0
- data/data/matrix_data/PAM490 +34 -0
- data/data/matrix_data/PAM50 +34 -0
- data/data/matrix_data/PAM500 +34 -0
- data/data/matrix_data/PAM60 +34 -0
- data/data/matrix_data/PAM70 +25 -0
- data/data/matrix_data/PAM80 +34 -0
- data/data/matrix_data/PAM90 +34 -0
- data/ext/stride_align/backend_avx2.cpp +2 -0
- data/ext/stride_align/backend_avx512bwvl.cpp +2 -0
- data/ext/stride_align/backend_generic.cpp +3 -0
- data/ext/stride_align/backend_impl.hpp +983 -0
- data/ext/stride_align/backend_lasx.cpp +2 -0
- data/ext/stride_align/backend_lsx.cpp +2 -0
- data/ext/stride_align/backend_neon.cpp +2 -0
- data/ext/stride_align/backend_rvv.cpp +2 -0
- data/ext/stride_align/backend_sse41.cpp +2 -0
- data/ext/stride_align/backend_sve.cpp +2 -0
- data/ext/stride_align/backend_sve2.cpp +2 -0
- data/ext/stride_align/backend_vsx.cpp +2 -0
- data/ext/stride_align/beider_morse_impl.cpp +5 -0
- data/ext/stride_align/cpu_detect.cpp +172 -0
- data/ext/stride_align/cpu_detect.hpp +6 -0
- data/ext/stride_align/extconf.rb +114 -0
- data/ext/stride_align/target_profile.hpp +82 -0
- data/ext/stride_align/vendor/beider_morse_impl.cpp +1467 -0
- data/ext/stride_align/vendor/stride_align/alignment.hpp +199 -0
- data/ext/stride_align/vendor/stride_align/batch.hpp +812 -0
- data/ext/stride_align/vendor/stride_align/beider_morse.hpp +121 -0
- data/ext/stride_align/vendor/stride_align/caverphone.hpp +222 -0
- data/ext/stride_align/vendor/stride_align/cologne_phonetic.hpp +202 -0
- data/ext/stride_align/vendor/stride_align/core.hpp +731 -0
- data/ext/stride_align/vendor/stride_align/daitch_mokotoff.hpp +631 -0
- data/ext/stride_align/vendor/stride_align/double_metaphone.hpp +796 -0
- data/ext/stride_align/vendor/stride_align/dtw.hpp +300 -0
- data/ext/stride_align/vendor/stride_align/encoded.hpp +235 -0
- data/ext/stride_align/vendor/stride_align/hamming.hpp +55 -0
- data/ext/stride_align/vendor/stride_align/indel.hpp +1200 -0
- data/ext/stride_align/vendor/stride_align/jaro.hpp +517 -0
- data/ext/stride_align/vendor/stride_align/lcs.hpp +159 -0
- data/ext/stride_align/vendor/stride_align/levenshtein.hpp +1247 -0
- data/ext/stride_align/vendor/stride_align/levenshtein_prepared.hpp +193 -0
- data/ext/stride_align/vendor/stride_align/match_rating.hpp +168 -0
- data/ext/stride_align/vendor/stride_align/metaphone.hpp +291 -0
- data/ext/stride_align/vendor/stride_align/ngram.hpp +176 -0
- data/ext/stride_align/vendor/stride_align/nysiis.hpp +199 -0
- data/ext/stride_align/vendor/stride_align/pairwise_alignment.hpp +465 -0
- data/ext/stride_align/vendor/stride_align/partial_ratio.hpp +486 -0
- data/ext/stride_align/vendor/stride_align/ratcliff_obershelp.hpp +101 -0
- data/ext/stride_align/vendor/stride_align/soundex.hpp +108 -0
- data/ext/stride_align/vendor/stride_align/token_ratios.hpp +445 -0
- data/ext/stride_align/vendor/stride_align/types.hpp +16 -0
- data/ext/stride_align/vendor/stride_align/utf8.hpp +512 -0
- data/ext/stride_align/vendor/stride_align/wratio.hpp +363 -0
- data/lib/stride_align/algorithms.rb +296 -0
- data/lib/stride_align/alignment_path.rb +217 -0
- data/lib/stride_align/backend.rb +47 -0
- data/lib/stride_align/batch.rb +705 -0
- data/lib/stride_align/core.rb +180 -0
- data/lib/stride_align/keyboard.rb +200 -0
- data/lib/stride_align/matrices.rb +403 -0
- data/lib/stride_align/version.rb +5 -0
- data/lib/stride_align.rb +87 -0
- metadata +231 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <array>
|
|
5
|
+
#include <cstddef>
|
|
6
|
+
#include <cstdint>
|
|
7
|
+
#include <initializer_list>
|
|
8
|
+
#include <limits>
|
|
9
|
+
#include <span>
|
|
10
|
+
#include <stdexcept>
|
|
11
|
+
#include <utility>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
#include "stride_align/types.hpp"
|
|
15
|
+
|
|
16
|
+
namespace stride_align::pairwise_alignment {
|
|
17
|
+
|
|
18
|
+
template <typename Token, bool Local>
|
|
19
|
+
inline Score linear_score_scalar(
|
|
20
|
+
std::span<const Token> query,
|
|
21
|
+
std::span<const Token> target,
|
|
22
|
+
Score match_score,
|
|
23
|
+
Score mismatch_score,
|
|
24
|
+
Score gap_score) {
|
|
25
|
+
std::vector<Score> previous(target.size() + 1U, 0);
|
|
26
|
+
std::vector<Score> current(target.size() + 1U, 0);
|
|
27
|
+
if constexpr (!Local) {
|
|
28
|
+
for (std::size_t column = 1; column <= target.size(); ++column) {
|
|
29
|
+
previous[column] = static_cast<Score>(column) * gap_score;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Score best = 0;
|
|
34
|
+
for (std::size_t row = 1; row <= query.size(); ++row) {
|
|
35
|
+
current[0] = Local ? 0 : static_cast<Score>(row) * gap_score;
|
|
36
|
+
for (std::size_t column = 1; column <= target.size(); ++column) {
|
|
37
|
+
const Score diagonal = previous[column - 1U] +
|
|
38
|
+
(query[row - 1U] == target[column - 1U] ? match_score : mismatch_score);
|
|
39
|
+
const Score up = previous[column] + gap_score;
|
|
40
|
+
const Score left = current[column - 1U] + gap_score;
|
|
41
|
+
Score value = std::max({diagonal, up, left});
|
|
42
|
+
if constexpr (Local) {
|
|
43
|
+
value = std::max<Score>(0, value);
|
|
44
|
+
best = std::max(best, value);
|
|
45
|
+
}
|
|
46
|
+
current[column] = value;
|
|
47
|
+
}
|
|
48
|
+
std::swap(previous, current);
|
|
49
|
+
}
|
|
50
|
+
if constexpr (Local) return best;
|
|
51
|
+
return previous.back();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
namespace detail {
|
|
55
|
+
|
|
56
|
+
inline constexpr std::size_t kWavefrontThreshold = 1024U;
|
|
57
|
+
|
|
58
|
+
inline bool use_wavefront(std::size_t query_size, std::size_t target_size) noexcept {
|
|
59
|
+
if (std::min(query_size, target_size) < 16U) return false;
|
|
60
|
+
return query_size > kWavefrontThreshold / target_size ||
|
|
61
|
+
query_size * target_size >= kWavefrontThreshold;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
inline std::uint64_t magnitude(Score value) noexcept {
|
|
65
|
+
if (value >= 0) return static_cast<std::uint64_t>(value);
|
|
66
|
+
return static_cast<std::uint64_t>(-(value + 1)) + 1U;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
template <typename Cell>
|
|
70
|
+
inline bool conservative_score_bound_fits(
|
|
71
|
+
std::size_t query_size,
|
|
72
|
+
std::size_t target_size,
|
|
73
|
+
std::initializer_list<Score> scores) noexcept {
|
|
74
|
+
std::uint64_t maximum_magnitude = 0;
|
|
75
|
+
for (const Score score : scores) {
|
|
76
|
+
maximum_magnitude = std::max(maximum_magnitude, magnitude(score));
|
|
77
|
+
}
|
|
78
|
+
if (maximum_magnitude == 0U) return true;
|
|
79
|
+
|
|
80
|
+
constexpr auto kCellMaximum = static_cast<std::uint64_t>(
|
|
81
|
+
std::numeric_limits<Cell>::max());
|
|
82
|
+
if (query_size > kCellMaximum || target_size > kCellMaximum) return false;
|
|
83
|
+
const std::uint64_t steps =
|
|
84
|
+
static_cast<std::uint64_t>(query_size) + target_size;
|
|
85
|
+
return steps <= kCellMaximum / maximum_magnitude;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
inline std::uint64_t saturating_multiply(
|
|
89
|
+
std::uint64_t lhs,
|
|
90
|
+
std::uint64_t rhs) noexcept {
|
|
91
|
+
constexpr auto kMaximum = std::numeric_limits<std::uint64_t>::max();
|
|
92
|
+
if (lhs != 0U && rhs > kMaximum / lhs) return kMaximum;
|
|
93
|
+
return lhs * rhs;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
inline std::uint64_t saturating_add(
|
|
97
|
+
std::uint64_t lhs,
|
|
98
|
+
std::uint64_t rhs) noexcept {
|
|
99
|
+
constexpr auto kMaximum = std::numeric_limits<std::uint64_t>::max();
|
|
100
|
+
if (rhs > kMaximum - lhs) return kMaximum;
|
|
101
|
+
return lhs + rhs;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
template <typename Cell>
|
|
105
|
+
inline bool linear_score_bound_fits(
|
|
106
|
+
std::size_t query_size,
|
|
107
|
+
std::size_t target_size,
|
|
108
|
+
Score match_score,
|
|
109
|
+
Score mismatch_score,
|
|
110
|
+
Score gap_score) noexcept {
|
|
111
|
+
const auto common = static_cast<std::uint64_t>(
|
|
112
|
+
std::min(query_size, target_size));
|
|
113
|
+
const auto difference = static_cast<std::uint64_t>(
|
|
114
|
+
std::max(query_size, target_size) - std::min(query_size, target_size));
|
|
115
|
+
const auto total = saturating_add(
|
|
116
|
+
static_cast<std::uint64_t>(query_size),
|
|
117
|
+
static_cast<std::uint64_t>(target_size));
|
|
118
|
+
const std::uint64_t substitution = std::max(
|
|
119
|
+
magnitude(match_score), magnitude(mismatch_score));
|
|
120
|
+
const std::uint64_t gap = magnitude(gap_score);
|
|
121
|
+
|
|
122
|
+
// A path either substitutes a shared position or consumes its symbols as
|
|
123
|
+
// gaps. The maximum absolute bound therefore occurs at an endpoint: all
|
|
124
|
+
// gaps, or every shared position substituted plus the length difference.
|
|
125
|
+
const std::uint64_t all_gaps = saturating_multiply(total, gap);
|
|
126
|
+
const std::uint64_t all_substitutions = saturating_add(
|
|
127
|
+
saturating_multiply(common, substitution),
|
|
128
|
+
saturating_multiply(difference, gap));
|
|
129
|
+
const std::uint64_t bound = std::max(all_gaps, all_substitutions);
|
|
130
|
+
return bound <= static_cast<std::uint64_t>(
|
|
131
|
+
std::numeric_limits<Cell>::max());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
template <typename Cell>
|
|
135
|
+
inline constexpr Cell negative_infinity_v = std::numeric_limits<Cell>::lowest();
|
|
136
|
+
|
|
137
|
+
template <typename Cell>
|
|
138
|
+
inline Cell add(Cell value, Cell delta) noexcept {
|
|
139
|
+
return value == negative_infinity_v<Cell>
|
|
140
|
+
? negative_infinity_v<Cell>
|
|
141
|
+
: static_cast<Cell>(value + delta);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
template <typename Token, typename Cell, bool Local>
|
|
145
|
+
Score linear_score_wavefront(
|
|
146
|
+
std::span<const Token> query,
|
|
147
|
+
std::span<const Token> target,
|
|
148
|
+
Score match_score,
|
|
149
|
+
Score mismatch_score,
|
|
150
|
+
Score gap_score) {
|
|
151
|
+
if (query.size() > target.size()) std::swap(query, target);
|
|
152
|
+
if (query.size() > std::numeric_limits<std::size_t>::max() - target.size()) {
|
|
153
|
+
throw std::length_error("alignment dimensions overflow");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const std::size_t query_size = query.size();
|
|
157
|
+
const std::size_t target_size = target.size();
|
|
158
|
+
const std::size_t final_diagonal = query_size + target_size;
|
|
159
|
+
std::vector<Token> reversed_target(target.rbegin(), target.rend());
|
|
160
|
+
|
|
161
|
+
const auto make_diagonal = [&] {
|
|
162
|
+
return std::vector<Cell>(query_size + 1U, negative_infinity_v<Cell>);
|
|
163
|
+
};
|
|
164
|
+
std::array<std::vector<Cell>, 3> h{
|
|
165
|
+
make_diagonal(), make_diagonal(), make_diagonal()};
|
|
166
|
+
h[0][0] = 0;
|
|
167
|
+
Cell best = 0;
|
|
168
|
+
|
|
169
|
+
const Cell match = static_cast<Cell>(match_score);
|
|
170
|
+
const Cell mismatch = static_cast<Cell>(mismatch_score);
|
|
171
|
+
const Cell gap = static_cast<Cell>(gap_score);
|
|
172
|
+
|
|
173
|
+
for (std::size_t diagonal = 1U; diagonal <= final_diagonal; ++diagonal) {
|
|
174
|
+
auto& current = h[diagonal % 3U];
|
|
175
|
+
const auto& previous = h[(diagonal - 1U) % 3U];
|
|
176
|
+
const auto& previous2 = h[(diagonal + 1U) % 3U];
|
|
177
|
+
const std::size_t first = diagonal > target_size
|
|
178
|
+
? diagonal - target_size
|
|
179
|
+
: 0U;
|
|
180
|
+
const std::size_t last = std::min(query_size, diagonal);
|
|
181
|
+
|
|
182
|
+
if (first == 0U) {
|
|
183
|
+
current[0] = Local
|
|
184
|
+
? Cell{0}
|
|
185
|
+
: static_cast<Cell>(static_cast<Score>(diagonal) * gap_score);
|
|
186
|
+
}
|
|
187
|
+
if (last == diagonal) {
|
|
188
|
+
current[last] = Local
|
|
189
|
+
? Cell{0}
|
|
190
|
+
: static_cast<Cell>(static_cast<Score>(diagonal) * gap_score);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const std::size_t interior_first = std::max<std::size_t>(1U, first);
|
|
194
|
+
const std::size_t interior_last = std::min(last, diagonal - 1U);
|
|
195
|
+
if (interior_first > interior_last) continue;
|
|
196
|
+
|
|
197
|
+
#if defined(__GNUC__) && !defined(__clang__)
|
|
198
|
+
#pragma GCC ivdep
|
|
199
|
+
#endif
|
|
200
|
+
for (std::size_t row = interior_first; row <= interior_last; ++row) {
|
|
201
|
+
const std::size_t column = diagonal - row;
|
|
202
|
+
const Cell substitution = query[row - 1U] ==
|
|
203
|
+
reversed_target[target_size - column]
|
|
204
|
+
? match
|
|
205
|
+
: mismatch;
|
|
206
|
+
const Cell diagonal_score = add(previous2[row - 1U], substitution);
|
|
207
|
+
const Cell up = add(previous[row - 1U], gap);
|
|
208
|
+
const Cell left = add(previous[row], gap);
|
|
209
|
+
Cell value = std::max(diagonal_score, up);
|
|
210
|
+
value = std::max(value, left);
|
|
211
|
+
if constexpr (Local) {
|
|
212
|
+
value = std::max<Cell>(0, value);
|
|
213
|
+
best = std::max(best, value);
|
|
214
|
+
}
|
|
215
|
+
current[row] = value;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if constexpr (Local) return static_cast<Score>(best);
|
|
220
|
+
return static_cast<Score>(h[final_diagonal % 3U][query_size]);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
} // namespace detail
|
|
224
|
+
|
|
225
|
+
template <typename Token, bool Local>
|
|
226
|
+
inline Score linear_score(
|
|
227
|
+
std::span<const Token> query,
|
|
228
|
+
std::span<const Token> target,
|
|
229
|
+
Score match_score,
|
|
230
|
+
Score mismatch_score,
|
|
231
|
+
Score gap_score) {
|
|
232
|
+
if (!detail::use_wavefront(query.size(), target.size())) {
|
|
233
|
+
return linear_score_scalar<Token, Local>(
|
|
234
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
235
|
+
}
|
|
236
|
+
if (detail::linear_score_bound_fits<std::int8_t>(
|
|
237
|
+
query.size(), target.size(), match_score, mismatch_score, gap_score)) {
|
|
238
|
+
return detail::linear_score_wavefront<Token, std::int8_t, Local>(
|
|
239
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
240
|
+
}
|
|
241
|
+
if (detail::linear_score_bound_fits<std::int16_t>(
|
|
242
|
+
query.size(), target.size(), match_score, mismatch_score, gap_score)) {
|
|
243
|
+
return detail::linear_score_wavefront<Token, std::int16_t, Local>(
|
|
244
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
245
|
+
}
|
|
246
|
+
if (detail::linear_score_bound_fits<std::int32_t>(
|
|
247
|
+
query.size(), target.size(), match_score, mismatch_score, gap_score)) {
|
|
248
|
+
return detail::linear_score_wavefront<Token, std::int32_t, Local>(
|
|
249
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
250
|
+
}
|
|
251
|
+
return detail::linear_score_wavefront<Token, Score, Local>(
|
|
252
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
inline constexpr Score kNegativeInfinity = std::numeric_limits<Score>::min() / 4;
|
|
256
|
+
|
|
257
|
+
inline Score add_or_negative_infinity(Score value, Score delta) noexcept {
|
|
258
|
+
return value == kNegativeInfinity ? kNegativeInfinity : value + delta;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
inline Score gap_cost(
|
|
262
|
+
std::size_t length,
|
|
263
|
+
Score gap_open_score,
|
|
264
|
+
Score gap_extend_score) noexcept {
|
|
265
|
+
if (length == 0U) return 0;
|
|
266
|
+
return gap_open_score + static_cast<Score>(length - 1U) * gap_extend_score;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
template <typename Token, bool Local>
|
|
270
|
+
inline Score affine_score_scalar(
|
|
271
|
+
std::span<const Token> query,
|
|
272
|
+
std::span<const Token> target,
|
|
273
|
+
Score match_score,
|
|
274
|
+
Score mismatch_score,
|
|
275
|
+
Score gap_open_score,
|
|
276
|
+
Score gap_extend_score) {
|
|
277
|
+
const std::size_t columns = target.size() + 1U;
|
|
278
|
+
std::vector<Score> previous_h(columns, 0);
|
|
279
|
+
std::vector<Score> current_h(columns, 0);
|
|
280
|
+
std::vector<Score> previous_vertical(columns, kNegativeInfinity);
|
|
281
|
+
std::vector<Score> current_vertical(columns, kNegativeInfinity);
|
|
282
|
+
|
|
283
|
+
if constexpr (!Local) {
|
|
284
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
285
|
+
previous_h[column] = gap_cost(column, gap_open_score, gap_extend_score);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
Score best = 0;
|
|
290
|
+
for (std::size_t row = 1; row <= query.size(); ++row) {
|
|
291
|
+
current_h[0] = Local ? 0 : gap_cost(row, gap_open_score, gap_extend_score);
|
|
292
|
+
current_vertical[0] = Local ? kNegativeInfinity : current_h[0];
|
|
293
|
+
Score horizontal = kNegativeInfinity;
|
|
294
|
+
|
|
295
|
+
for (std::size_t column = 1; column <= target.size(); ++column) {
|
|
296
|
+
current_vertical[column] = std::max(
|
|
297
|
+
previous_h[column] + gap_open_score,
|
|
298
|
+
add_or_negative_infinity(previous_vertical[column], gap_extend_score));
|
|
299
|
+
horizontal = std::max(
|
|
300
|
+
current_h[column - 1U] + gap_open_score,
|
|
301
|
+
add_or_negative_infinity(horizontal, gap_extend_score));
|
|
302
|
+
const Score diagonal = previous_h[column - 1U] +
|
|
303
|
+
(query[row - 1U] == target[column - 1U] ? match_score : mismatch_score);
|
|
304
|
+
Score value = std::max({diagonal, current_vertical[column], horizontal});
|
|
305
|
+
if constexpr (Local) {
|
|
306
|
+
value = std::max<Score>(0, value);
|
|
307
|
+
best = std::max(best, value);
|
|
308
|
+
}
|
|
309
|
+
current_h[column] = value;
|
|
310
|
+
}
|
|
311
|
+
std::swap(previous_h, current_h);
|
|
312
|
+
std::swap(previous_vertical, current_vertical);
|
|
313
|
+
}
|
|
314
|
+
if constexpr (Local) return best;
|
|
315
|
+
return previous_h.back();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
namespace detail {
|
|
319
|
+
|
|
320
|
+
template <typename Token, typename Cell, bool Local>
|
|
321
|
+
Score affine_score_wavefront(
|
|
322
|
+
std::span<const Token> query,
|
|
323
|
+
std::span<const Token> target,
|
|
324
|
+
Score match_score,
|
|
325
|
+
Score mismatch_score,
|
|
326
|
+
Score gap_open_score,
|
|
327
|
+
Score gap_extend_score) {
|
|
328
|
+
if (query.size() > target.size()) std::swap(query, target);
|
|
329
|
+
if (query.size() > std::numeric_limits<std::size_t>::max() - target.size()) {
|
|
330
|
+
throw std::length_error("alignment dimensions overflow");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const std::size_t query_size = query.size();
|
|
334
|
+
const std::size_t target_size = target.size();
|
|
335
|
+
const std::size_t final_diagonal = query_size + target_size;
|
|
336
|
+
std::vector<Token> reversed_target(target.rbegin(), target.rend());
|
|
337
|
+
|
|
338
|
+
const auto make_diagonal = [&] {
|
|
339
|
+
return std::vector<Cell>(query_size + 1U, negative_infinity_v<Cell>);
|
|
340
|
+
};
|
|
341
|
+
std::array<std::vector<Cell>, 3> h{
|
|
342
|
+
make_diagonal(), make_diagonal(), make_diagonal()};
|
|
343
|
+
std::array<std::vector<Cell>, 3> vertical{
|
|
344
|
+
make_diagonal(), make_diagonal(), make_diagonal()};
|
|
345
|
+
std::array<std::vector<Cell>, 3> horizontal{
|
|
346
|
+
make_diagonal(), make_diagonal(), make_diagonal()};
|
|
347
|
+
h[0][0] = 0;
|
|
348
|
+
Cell best = 0;
|
|
349
|
+
|
|
350
|
+
const Cell match = static_cast<Cell>(match_score);
|
|
351
|
+
const Cell mismatch = static_cast<Cell>(mismatch_score);
|
|
352
|
+
const Cell gap_open = static_cast<Cell>(gap_open_score);
|
|
353
|
+
const Cell gap_extend = static_cast<Cell>(gap_extend_score);
|
|
354
|
+
|
|
355
|
+
for (std::size_t diagonal = 1U; diagonal <= final_diagonal; ++diagonal) {
|
|
356
|
+
auto& current_h = h[diagonal % 3U];
|
|
357
|
+
auto& current_vertical = vertical[diagonal % 3U];
|
|
358
|
+
auto& current_horizontal = horizontal[diagonal % 3U];
|
|
359
|
+
const auto& previous_h = h[(diagonal - 1U) % 3U];
|
|
360
|
+
const auto& previous_vertical = vertical[(diagonal - 1U) % 3U];
|
|
361
|
+
const auto& previous_horizontal = horizontal[(diagonal - 1U) % 3U];
|
|
362
|
+
const auto& previous2_h = h[(diagonal + 1U) % 3U];
|
|
363
|
+
const std::size_t first = diagonal > target_size
|
|
364
|
+
? diagonal - target_size
|
|
365
|
+
: 0U;
|
|
366
|
+
const std::size_t last = std::min(query_size, diagonal);
|
|
367
|
+
|
|
368
|
+
if (first == 0U) {
|
|
369
|
+
current_h[0] = Local
|
|
370
|
+
? Cell{0}
|
|
371
|
+
: static_cast<Cell>(gap_cost(
|
|
372
|
+
diagonal, gap_open_score, gap_extend_score));
|
|
373
|
+
current_vertical[0] = negative_infinity_v<Cell>;
|
|
374
|
+
current_horizontal[0] = Local
|
|
375
|
+
? negative_infinity_v<Cell>
|
|
376
|
+
: current_h[0];
|
|
377
|
+
}
|
|
378
|
+
if (last == diagonal) {
|
|
379
|
+
current_h[last] = Local
|
|
380
|
+
? Cell{0}
|
|
381
|
+
: static_cast<Cell>(gap_cost(
|
|
382
|
+
diagonal, gap_open_score, gap_extend_score));
|
|
383
|
+
current_vertical[last] = Local
|
|
384
|
+
? negative_infinity_v<Cell>
|
|
385
|
+
: current_h[last];
|
|
386
|
+
current_horizontal[last] = negative_infinity_v<Cell>;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const std::size_t interior_first = std::max<std::size_t>(1U, first);
|
|
390
|
+
const std::size_t interior_last = std::min(last, diagonal - 1U);
|
|
391
|
+
if (interior_first > interior_last) continue;
|
|
392
|
+
|
|
393
|
+
#if defined(__GNUC__) && !defined(__clang__)
|
|
394
|
+
#pragma GCC ivdep
|
|
395
|
+
#endif
|
|
396
|
+
for (std::size_t row = interior_first; row <= interior_last; ++row) {
|
|
397
|
+
const std::size_t column = diagonal - row;
|
|
398
|
+
const Cell substitution = query[row - 1U] ==
|
|
399
|
+
reversed_target[target_size - column]
|
|
400
|
+
? match
|
|
401
|
+
: mismatch;
|
|
402
|
+
const Cell vertical_score = std::max(
|
|
403
|
+
add(previous_h[row - 1U], gap_open),
|
|
404
|
+
add(previous_vertical[row - 1U], gap_extend));
|
|
405
|
+
const Cell horizontal_score = std::max(
|
|
406
|
+
add(previous_h[row], gap_open),
|
|
407
|
+
add(previous_horizontal[row], gap_extend));
|
|
408
|
+
const Cell diagonal_score = add(previous2_h[row - 1U], substitution);
|
|
409
|
+
Cell value = std::max(diagonal_score, vertical_score);
|
|
410
|
+
value = std::max(value, horizontal_score);
|
|
411
|
+
if constexpr (Local) {
|
|
412
|
+
value = std::max<Cell>(0, value);
|
|
413
|
+
best = std::max(best, value);
|
|
414
|
+
}
|
|
415
|
+
current_h[row] = value;
|
|
416
|
+
current_vertical[row] = vertical_score;
|
|
417
|
+
current_horizontal[row] = horizontal_score;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if constexpr (Local) return static_cast<Score>(best);
|
|
422
|
+
return static_cast<Score>(h[final_diagonal % 3U][query_size]);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
} // namespace detail
|
|
426
|
+
|
|
427
|
+
template <typename Token, bool Local>
|
|
428
|
+
inline Score affine_score(
|
|
429
|
+
std::span<const Token> query,
|
|
430
|
+
std::span<const Token> target,
|
|
431
|
+
Score match_score,
|
|
432
|
+
Score mismatch_score,
|
|
433
|
+
Score gap_open_score,
|
|
434
|
+
Score gap_extend_score) {
|
|
435
|
+
if (!detail::use_wavefront(query.size(), target.size())) {
|
|
436
|
+
return affine_score_scalar<Token, Local>(
|
|
437
|
+
query, target, match_score, mismatch_score,
|
|
438
|
+
gap_open_score, gap_extend_score);
|
|
439
|
+
}
|
|
440
|
+
const auto scores = {
|
|
441
|
+
match_score, mismatch_score, gap_open_score, gap_extend_score};
|
|
442
|
+
if (detail::conservative_score_bound_fits<std::int8_t>(
|
|
443
|
+
query.size(), target.size(), scores)) {
|
|
444
|
+
return detail::affine_score_wavefront<Token, std::int8_t, Local>(
|
|
445
|
+
query, target, match_score, mismatch_score,
|
|
446
|
+
gap_open_score, gap_extend_score);
|
|
447
|
+
}
|
|
448
|
+
if (detail::conservative_score_bound_fits<std::int16_t>(
|
|
449
|
+
query.size(), target.size(), scores)) {
|
|
450
|
+
return detail::affine_score_wavefront<Token, std::int16_t, Local>(
|
|
451
|
+
query, target, match_score, mismatch_score,
|
|
452
|
+
gap_open_score, gap_extend_score);
|
|
453
|
+
}
|
|
454
|
+
if (detail::conservative_score_bound_fits<std::int32_t>(
|
|
455
|
+
query.size(), target.size(), scores)) {
|
|
456
|
+
return detail::affine_score_wavefront<Token, std::int32_t, Local>(
|
|
457
|
+
query, target, match_score, mismatch_score,
|
|
458
|
+
gap_open_score, gap_extend_score);
|
|
459
|
+
}
|
|
460
|
+
return detail::affine_score_wavefront<Token, Score, Local>(
|
|
461
|
+
query, target, match_score, mismatch_score,
|
|
462
|
+
gap_open_score, gap_extend_score);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
} // namespace stride_align::pairwise_alignment
|