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,705 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StrideAlign
|
|
4
|
+
module Scorer
|
|
5
|
+
LEVENSHTEIN = 0
|
|
6
|
+
LEVENSHTEIN_NORMALIZED = 1
|
|
7
|
+
DAMERAU_LEVENSHTEIN = 2
|
|
8
|
+
DAMERAU_LEVENSHTEIN_NORMALIZED = 3
|
|
9
|
+
HAMMING = 4
|
|
10
|
+
HAMMING_NORMALIZED = 5
|
|
11
|
+
JARO = 6
|
|
12
|
+
JARO_WINKLER = 7
|
|
13
|
+
INDEL = 8
|
|
14
|
+
INDEL_NORMALIZED = 9
|
|
15
|
+
TRUE_DAMERAU_LEVENSHTEIN = 10
|
|
16
|
+
TRUE_DAMERAU_LEVENSHTEIN_NORMALIZED = 11
|
|
17
|
+
SMITH_WATERMAN = 12
|
|
18
|
+
SMITH_WATERMAN_NORMALIZED = 13
|
|
19
|
+
NEEDLEMAN_WUNSCH = 14
|
|
20
|
+
NEEDLEMAN_WUNSCH_NORMALIZED = 15
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
ScorerEntry = Struct.new(
|
|
24
|
+
:names, :id, :batch_method, :higher, :normalized, keyword_init: true
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
class LevenshteinScorer
|
|
28
|
+
attr_reader :query
|
|
29
|
+
|
|
30
|
+
def initialize(query)
|
|
31
|
+
@query = StrideAlign.__send__(:scalar_query, query)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def distance(target, score_cutoff: nil)
|
|
35
|
+
StrideAlign.levenshtein_score(@query, target, score_cutoff: score_cutoff)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def normalized_distance(target, score_cutoff: nil)
|
|
39
|
+
StrideAlign.levenshtein_normalized_score(
|
|
40
|
+
@query, target, score_cutoff: score_cutoff
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def distances(targets, score_cutoff: nil)
|
|
45
|
+
StrideAlign.levenshtein_scores(@query, targets, score_cutoff: score_cutoff)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def normalized_distances(targets, score_cutoff: nil)
|
|
49
|
+
StrideAlign.levenshtein_normalized_scores(
|
|
50
|
+
@query, targets, score_cutoff: score_cutoff
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class << self
|
|
56
|
+
def levenshtein_score(query, target, score_cutoff: nil)
|
|
57
|
+
cutoff = score_cutoff.nil? ? nil : non_negative_integer(score_cutoff, "score_cutoff")
|
|
58
|
+
pairwise(query, target) do |left, right|
|
|
59
|
+
value = __native_backend.levenshtein(left, right)
|
|
60
|
+
value.nil? || cutoff.nil? ? value : [value, cutoff + 1].min
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def levenshtein_normalized_score(query, target, score_cutoff: nil)
|
|
65
|
+
cutoff = score_cutoff.nil? ? nil : non_negative_integer(score_cutoff, "score_cutoff")
|
|
66
|
+
pairwise(query, target) do |left, right|
|
|
67
|
+
if left.nil? || right.nil?
|
|
68
|
+
nil
|
|
69
|
+
else
|
|
70
|
+
distance = __native_backend.levenshtein(left, right)
|
|
71
|
+
distance = [distance, cutoff + 1].min unless cutoff.nil?
|
|
72
|
+
longest = [left.each_char.count, right.each_char.count].max
|
|
73
|
+
longest.zero? ? 1.0 : [0.0, 1.0 - distance.to_f / longest].max
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def damerau_levenshtein_score(query, target)
|
|
79
|
+
stride_osa(query, target)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def damerau_levenshtein_normalized_score(query, target)
|
|
83
|
+
stride_osa_similarity(query, target)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def true_damerau_levenshtein_score(query, target)
|
|
87
|
+
stride_true_damerau_levenshtein(query, target)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def true_damerau_levenshtein_normalized_score(query, target)
|
|
91
|
+
stride_true_damerau_levenshtein_similarity(query, target)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def indel_score(query, target, score_cutoff: nil)
|
|
95
|
+
cutoff = score_cutoff.nil? ? nil : non_negative_integer(score_cutoff, "score_cutoff")
|
|
96
|
+
pairwise(query, target) do |left, right|
|
|
97
|
+
value = __native_backend.indel(left, right)
|
|
98
|
+
value.nil? || cutoff.nil? ? value : [value, cutoff + 1].min
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def indel_normalized_score(query, target, score_cutoff: nil)
|
|
103
|
+
unless score_cutoff.nil? ||
|
|
104
|
+
(score_cutoff.is_a?(Numeric) && score_cutoff.finite? &&
|
|
105
|
+
score_cutoff.between?(0, 1))
|
|
106
|
+
raise ArgumentError, "score_cutoff must be between 0 and 1"
|
|
107
|
+
end
|
|
108
|
+
pairwise(query, target) do |left, right|
|
|
109
|
+
value = __native_backend.indel_similarity(left, right)
|
|
110
|
+
if value.nil? || score_cutoff.nil?
|
|
111
|
+
value
|
|
112
|
+
else
|
|
113
|
+
value < score_cutoff ? 0.0 : value
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def hamming_score(query, target)
|
|
119
|
+
stride_hamming(query, target)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def hamming_normalized_score(query, target)
|
|
123
|
+
stride_hamming_similarity(query, target)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def jaro_similarity(query, target)
|
|
127
|
+
stride_jaro(query, target)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def jaro_winkler_similarity(query, target, prefix_weight: 0.1,
|
|
131
|
+
prefix_threshold: 0.7, prefix_cap: 4)
|
|
132
|
+
stride_jaro_winkler(
|
|
133
|
+
query, target, prefix_weight: prefix_weight,
|
|
134
|
+
prefix_threshold: prefix_threshold, prefix_cap: prefix_cap
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def smith_waterman_score(query, target, match_score: 2,
|
|
139
|
+
mismatch_score: -1, gap_score: -1,
|
|
140
|
+
gap_open_score: nil, gap_extend_score: nil,
|
|
141
|
+
width: nil, matrix: nil)
|
|
142
|
+
if matrix
|
|
143
|
+
matrix_kwargs_clean(match_score, mismatch_score, width)
|
|
144
|
+
return matrix_pairwise(
|
|
145
|
+
query, target, matrix, true, gap_score: gap_score,
|
|
146
|
+
gap_open_score: gap_open_score, gap_extend_score: gap_extend_score
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
validate_width(width)
|
|
150
|
+
alignment_score(
|
|
151
|
+
query, target, true, match_score, mismatch_score, gap_score,
|
|
152
|
+
gap_open_score, gap_extend_score
|
|
153
|
+
)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def needleman_wunsch_score(query, target, match_score: 2,
|
|
157
|
+
mismatch_score: -1, gap_score: -1,
|
|
158
|
+
gap_open_score: nil, gap_extend_score: nil,
|
|
159
|
+
width: nil, matrix: nil)
|
|
160
|
+
if matrix
|
|
161
|
+
matrix_kwargs_clean(match_score, mismatch_score, width)
|
|
162
|
+
return matrix_pairwise(
|
|
163
|
+
query, target, matrix, false, gap_score: gap_score,
|
|
164
|
+
gap_open_score: gap_open_score, gap_extend_score: gap_extend_score
|
|
165
|
+
)
|
|
166
|
+
end
|
|
167
|
+
validate_width(width)
|
|
168
|
+
alignment_score(
|
|
169
|
+
query, target, false, match_score, mismatch_score, gap_score,
|
|
170
|
+
gap_open_score, gap_extend_score
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def smith_waterman_normalized_score(query, target, **kwargs)
|
|
175
|
+
match_score = kwargs.fetch(:match_score, 2)
|
|
176
|
+
raw = smith_waterman_score(query, target, **kwargs)
|
|
177
|
+
normalize_alignment_result(raw, query, target, true, match_score)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def needleman_wunsch_normalized_score(query, target, **kwargs)
|
|
181
|
+
match_score = kwargs.fetch(:match_score, 2)
|
|
182
|
+
raw = needleman_wunsch_score(query, target, **kwargs)
|
|
183
|
+
normalize_alignment_result(raw, query, target, false, match_score)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
alias smith_waterman_farrar_score smith_waterman_score
|
|
187
|
+
alias smith_waterman_farrar_normalized_score smith_waterman_normalized_score
|
|
188
|
+
|
|
189
|
+
def levenshtein_scores(query, targets, score_cutoff: nil)
|
|
190
|
+
one_to_many(query, targets) do |left, right|
|
|
191
|
+
levenshtein_score(left, right, score_cutoff: score_cutoff)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def levenshtein_normalized_scores(query, targets, score_cutoff: nil)
|
|
196
|
+
one_to_many(query, targets) do |left, right|
|
|
197
|
+
levenshtein_normalized_score(left, right, score_cutoff: score_cutoff)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def damerau_levenshtein_scores(query, targets)
|
|
202
|
+
one_to_many(query, targets) { |left, right| damerau_levenshtein_score(left, right) }
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def damerau_levenshtein_normalized_scores(query, targets)
|
|
206
|
+
one_to_many(query, targets) do |left, right|
|
|
207
|
+
damerau_levenshtein_normalized_score(left, right)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def true_damerau_levenshtein_scores(query, targets)
|
|
212
|
+
one_to_many(query, targets) do |left, right|
|
|
213
|
+
true_damerau_levenshtein_score(left, right)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def true_damerau_levenshtein_normalized_scores(query, targets)
|
|
218
|
+
one_to_many(query, targets) do |left, right|
|
|
219
|
+
true_damerau_levenshtein_normalized_score(left, right)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def indel_scores(query, targets, score_cutoff: nil)
|
|
224
|
+
one_to_many(query, targets) do |left, right|
|
|
225
|
+
indel_score(left, right, score_cutoff: score_cutoff)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def indel_normalized_scores(query, targets, score_cutoff: nil)
|
|
230
|
+
one_to_many(query, targets) do |left, right|
|
|
231
|
+
indel_normalized_score(left, right, score_cutoff: score_cutoff)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def hamming_scores(query, targets)
|
|
236
|
+
one_to_many(query, targets) { |left, right| hamming_score(left, right) }
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def hamming_normalized_scores(query, targets)
|
|
240
|
+
one_to_many(query, targets) { |left, right| hamming_normalized_score(left, right) }
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def jaro_similarities(query, targets)
|
|
244
|
+
one_to_many(query, targets) { |left, right| jaro_similarity(left, right) }
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def jaro_winkler_similarities(query, targets, **kwargs)
|
|
248
|
+
one_to_many(query, targets) do |left, right|
|
|
249
|
+
jaro_winkler_similarity(left, right, **kwargs)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def smith_waterman_scores(query, targets, **kwargs)
|
|
254
|
+
one_to_many(query, targets) do |left, right|
|
|
255
|
+
smith_waterman_score(left, right, **kwargs)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def smith_waterman_normalized_scores(query, targets, **kwargs)
|
|
260
|
+
one_to_many(query, targets) do |left, right|
|
|
261
|
+
smith_waterman_normalized_score(left, right, **kwargs)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
alias smith_waterman_farrar_scores smith_waterman_scores
|
|
266
|
+
alias smith_waterman_farrar_normalized_scores smith_waterman_normalized_scores
|
|
267
|
+
|
|
268
|
+
def needleman_wunsch_scores(query, targets, **kwargs)
|
|
269
|
+
one_to_many(query, targets) do |left, right|
|
|
270
|
+
needleman_wunsch_score(left, right, **kwargs)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def needleman_wunsch_normalized_scores(query, targets, **kwargs)
|
|
275
|
+
one_to_many(query, targets) do |left, right|
|
|
276
|
+
needleman_wunsch_normalized_score(left, right, **kwargs)
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def levenshtein_top_k(query, targets, k: 5)
|
|
281
|
+
top_k(query, targets, Scorer::LEVENSHTEIN, k: k)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def levenshtein_normalized_top_k(query, targets, k: 5)
|
|
285
|
+
top_k(query, targets, Scorer::LEVENSHTEIN_NORMALIZED, k: k)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def damerau_levenshtein_top_k(query, targets, k: 5)
|
|
289
|
+
top_k(query, targets, Scorer::DAMERAU_LEVENSHTEIN, k: k)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def damerau_levenshtein_normalized_top_k(query, targets, k: 5)
|
|
293
|
+
top_k(query, targets, Scorer::DAMERAU_LEVENSHTEIN_NORMALIZED, k: k)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def true_damerau_levenshtein_top_k(query, targets, k: 5)
|
|
297
|
+
top_k(query, targets, Scorer::TRUE_DAMERAU_LEVENSHTEIN, k: k)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def true_damerau_levenshtein_normalized_top_k(query, targets, k: 5)
|
|
301
|
+
top_k(query, targets, Scorer::TRUE_DAMERAU_LEVENSHTEIN_NORMALIZED, k: k)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def indel_top_k(query, targets, k: 5)
|
|
305
|
+
top_k(query, targets, Scorer::INDEL, k: k)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def indel_normalized_top_k(query, targets, k: 5)
|
|
309
|
+
top_k(query, targets, Scorer::INDEL_NORMALIZED, k: k)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def hamming_top_k(query, targets, k: 5)
|
|
313
|
+
top_k(query, targets, Scorer::HAMMING, k: k)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def hamming_normalized_top_k(query, targets, k: 5)
|
|
317
|
+
top_k(query, targets, Scorer::HAMMING_NORMALIZED, k: k)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def jaro_top_k(query, targets, k: 5)
|
|
321
|
+
top_k(query, targets, Scorer::JARO, k: k)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def jaro_winkler_top_k(query, targets, k: 5, **kwargs)
|
|
325
|
+
top_k(query, targets, Scorer::JARO_WINKLER, k: k, **kwargs)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def smith_waterman_top_k(query, targets, k: 5, **kwargs)
|
|
329
|
+
top_k(query, targets, Scorer::SMITH_WATERMAN, k: k, **kwargs)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def levenshtein_best(query, targets)
|
|
333
|
+
best(query, targets, Scorer::LEVENSHTEIN)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def levenshtein_normalized_best(query, targets)
|
|
337
|
+
best(query, targets, Scorer::LEVENSHTEIN_NORMALIZED)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def damerau_levenshtein_best(query, targets)
|
|
341
|
+
best(query, targets, Scorer::DAMERAU_LEVENSHTEIN)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def damerau_levenshtein_normalized_best(query, targets)
|
|
345
|
+
best(query, targets, Scorer::DAMERAU_LEVENSHTEIN_NORMALIZED)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def true_damerau_levenshtein_best(query, targets)
|
|
349
|
+
best(query, targets, Scorer::TRUE_DAMERAU_LEVENSHTEIN)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def true_damerau_levenshtein_normalized_best(query, targets)
|
|
353
|
+
best(query, targets, Scorer::TRUE_DAMERAU_LEVENSHTEIN_NORMALIZED)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def indel_best(query, targets)
|
|
357
|
+
best(query, targets, Scorer::INDEL)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def indel_normalized_best(query, targets)
|
|
361
|
+
best(query, targets, Scorer::INDEL_NORMALIZED)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def hamming_best(query, targets)
|
|
365
|
+
best(query, targets, Scorer::HAMMING)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def hamming_normalized_best(query, targets)
|
|
369
|
+
best(query, targets, Scorer::HAMMING_NORMALIZED)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def jaro_best(query, targets)
|
|
373
|
+
best(query, targets, Scorer::JARO)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def jaro_winkler_best(query, targets, **kwargs)
|
|
377
|
+
best(query, targets, Scorer::JARO_WINKLER, **kwargs)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def smith_waterman_best(query, targets, **kwargs)
|
|
381
|
+
best(query, targets, Scorer::SMITH_WATERMAN, **kwargs)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def extract(query, targets, scorer, k: 5, **kwargs)
|
|
385
|
+
top_k(query, targets, scorer, k: k, **kwargs)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def extract_best(query, targets, scorer, **kwargs)
|
|
389
|
+
best(query, targets, scorer, **kwargs)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def cdist(queries, targets, scorer: nil, matrix: nil,
|
|
393
|
+
match_score: 2, mismatch_score: -1, gap_score: -1,
|
|
394
|
+
gap_open_score: nil, gap_extend_score: nil, width: nil,
|
|
395
|
+
tqdm: nil, cpu_count: 0, prefix_weight: 0.1,
|
|
396
|
+
prefix_threshold: 0.7, prefix_cap: 4)
|
|
397
|
+
_ = cpu_count
|
|
398
|
+
queries = target_array(queries)
|
|
399
|
+
targets = target_array(targets)
|
|
400
|
+
if scorer.nil?
|
|
401
|
+
raise ArgumentError, "cdist requires either scorer or matrix" if matrix.nil?
|
|
402
|
+
scorer = Scorer::SMITH_WATERMAN
|
|
403
|
+
end
|
|
404
|
+
entry = resolve_scorer(scorer)
|
|
405
|
+
if matrix && ![Scorer::SMITH_WATERMAN, Scorer::NEEDLEMAN_WUNSCH].include?(entry.id)
|
|
406
|
+
raise ArgumentError,
|
|
407
|
+
"matrix cdist requires a Smith-Waterman or Needleman-Wunsch scorer"
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
progress = nil
|
|
411
|
+
if tqdm
|
|
412
|
+
raise ArgumentError, "tqdm must respond to call" unless tqdm.respond_to?(:call)
|
|
413
|
+
progress = tqdm.call(queries.length)
|
|
414
|
+
unless progress.respond_to?(:update) && progress.respond_to?(:close)
|
|
415
|
+
raise ArgumentError, "tqdm progress object must provide update and close"
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
begin
|
|
420
|
+
queries.map do |query|
|
|
421
|
+
options = {}
|
|
422
|
+
if (12..15).cover?(entry.id)
|
|
423
|
+
options.merge!(
|
|
424
|
+
match_score: match_score, mismatch_score: mismatch_score,
|
|
425
|
+
gap_score: gap_score, gap_open_score: gap_open_score,
|
|
426
|
+
gap_extend_score: gap_extend_score, width: width
|
|
427
|
+
)
|
|
428
|
+
options[:matrix] = matrix if matrix
|
|
429
|
+
elsif entry.id == Scorer::JARO_WINKLER
|
|
430
|
+
options.merge!(
|
|
431
|
+
prefix_weight: prefix_weight,
|
|
432
|
+
prefix_threshold: prefix_threshold,
|
|
433
|
+
prefix_cap: prefix_cap
|
|
434
|
+
)
|
|
435
|
+
end
|
|
436
|
+
row = if options.empty?
|
|
437
|
+
public_send(entry.batch_method, query, targets)
|
|
438
|
+
else
|
|
439
|
+
public_send(entry.batch_method, query, targets, **options)
|
|
440
|
+
end
|
|
441
|
+
progress.update(1) if progress
|
|
442
|
+
row
|
|
443
|
+
end
|
|
444
|
+
ensure
|
|
445
|
+
progress.close if progress
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def cdist_above_threshold(queries, targets, scorer: nil, threshold:,
|
|
450
|
+
matrix: nil, **kwargs)
|
|
451
|
+
if scorer.nil? && matrix.nil?
|
|
452
|
+
raise ArgumentError, "cdist_above_threshold requires either scorer or matrix"
|
|
453
|
+
end
|
|
454
|
+
resolved = scorer.nil? ? Scorer::SMITH_WATERMAN : scorer
|
|
455
|
+
entry = resolve_scorer(resolved)
|
|
456
|
+
if matrix.nil? && !entry.normalized
|
|
457
|
+
raise ArgumentError,
|
|
458
|
+
"cdist_above_threshold requires a normalized or similarity scorer"
|
|
459
|
+
end
|
|
460
|
+
finite_number(threshold, "threshold")
|
|
461
|
+
if matrix.nil? && !threshold.between?(0, 1)
|
|
462
|
+
raise ArgumentError, "threshold must be between 0 and 1"
|
|
463
|
+
end
|
|
464
|
+
clean_queries = target_array(queries)
|
|
465
|
+
clean_targets = target_array(targets)
|
|
466
|
+
scores = cdist(
|
|
467
|
+
clean_queries, clean_targets, scorer: resolved, matrix: matrix, **kwargs
|
|
468
|
+
)
|
|
469
|
+
output = []
|
|
470
|
+
scores.each_with_index do |row, query_index|
|
|
471
|
+
row.each_with_index do |score, target_index|
|
|
472
|
+
next if score.nil? || score < threshold
|
|
473
|
+
output << {
|
|
474
|
+
score: score, query: clean_queries[query_index],
|
|
475
|
+
target: clean_targets[target_index], query_index: query_index,
|
|
476
|
+
target_index: target_index
|
|
477
|
+
}
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
output
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def cdist_top_k(queries, targets, scorer: nil, k:, matrix: nil,
|
|
484
|
+
reject_duplicates: false, **kwargs)
|
|
485
|
+
if scorer.nil? && matrix.nil?
|
|
486
|
+
raise ArgumentError, "cdist_top_k requires either scorer or matrix"
|
|
487
|
+
end
|
|
488
|
+
resolved = scorer.nil? ? Scorer::SMITH_WATERMAN : scorer
|
|
489
|
+
entry = resolve_scorer(resolved)
|
|
490
|
+
if matrix.nil? && !entry.normalized
|
|
491
|
+
raise ArgumentError, "cdist_top_k requires a normalized or similarity scorer"
|
|
492
|
+
end
|
|
493
|
+
if matrix && reject_duplicates
|
|
494
|
+
raise ArgumentError, "reject_duplicates is not supported with matrix"
|
|
495
|
+
end
|
|
496
|
+
k = non_negative_integer(k, "k")
|
|
497
|
+
clean_queries = target_array(queries)
|
|
498
|
+
clean_targets = target_array(targets)
|
|
499
|
+
scores = cdist(
|
|
500
|
+
clean_queries, clean_targets, scorer: resolved, matrix: matrix, **kwargs
|
|
501
|
+
)
|
|
502
|
+
values = []
|
|
503
|
+
scores.each_with_index do |row, query_index|
|
|
504
|
+
row.each_with_index do |score, target_index|
|
|
505
|
+
next if score.nil?
|
|
506
|
+
next if reject_duplicates && clean_queries[query_index] == clean_targets[target_index]
|
|
507
|
+
values << {
|
|
508
|
+
score: score, query: clean_queries[query_index],
|
|
509
|
+
target: clean_targets[target_index], query_index: query_index,
|
|
510
|
+
target_index: target_index
|
|
511
|
+
}
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
values.sort_by { |item| [-item[:score], item[:query_index], item[:target_index]] }
|
|
515
|
+
.first(k)
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def cdist_top_k_per_query(queries, targets, scorer:, k: 5,
|
|
519
|
+
pruning: false, cpu_count: 0,
|
|
520
|
+
prefix_weight: 0.1,
|
|
521
|
+
prefix_threshold: 0.7, prefix_cap: 4)
|
|
522
|
+
_ = [pruning, cpu_count]
|
|
523
|
+
clean_queries = target_array(queries)
|
|
524
|
+
clean_targets = target_array(targets)
|
|
525
|
+
entry = resolve_scorer(scorer)
|
|
526
|
+
unless entry.normalized
|
|
527
|
+
raise ArgumentError,
|
|
528
|
+
"cdist_top_k_per_query requires a normalized or similarity scorer"
|
|
529
|
+
end
|
|
530
|
+
options = entry.id == Scorer::JARO_WINKLER ? {
|
|
531
|
+
prefix_weight: prefix_weight,
|
|
532
|
+
prefix_threshold: prefix_threshold,
|
|
533
|
+
prefix_cap: prefix_cap
|
|
534
|
+
} : {}
|
|
535
|
+
clean_queries.each_with_object({}) do |query, output|
|
|
536
|
+
if entry.id == Scorer::HAMMING_NORMALIZED
|
|
537
|
+
valid = clean_targets.each_index.select do |index|
|
|
538
|
+
!query.nil? && !clean_targets[index].nil? &&
|
|
539
|
+
clean_targets[index].each_char.count == query.each_char.count
|
|
540
|
+
end
|
|
541
|
+
subset = valid.map { |index| clean_targets[index] }
|
|
542
|
+
ranked = ranked(
|
|
543
|
+
subset, hamming_normalized_scores(query, subset), k, true
|
|
544
|
+
)
|
|
545
|
+
ranked.each { |item| item[:index] = valid[item[:index]] }
|
|
546
|
+
output[query] = ranked
|
|
547
|
+
else
|
|
548
|
+
output[query] = top_k(query, clean_targets, scorer, k: k, **options)
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
private
|
|
554
|
+
|
|
555
|
+
def matrix_kwargs_clean(match_score, mismatch_score, width)
|
|
556
|
+
return if match_score == 2 && mismatch_score == -1 && width.nil?
|
|
557
|
+
|
|
558
|
+
raise ArgumentError,
|
|
559
|
+
"match_score, mismatch_score, and width cannot be used with matrix"
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def alignment_score(query, target, local, match_score, mismatch_score,
|
|
563
|
+
gap_score, gap_open_score, gap_extend_score)
|
|
564
|
+
open_score, extend_score = resolve_gaps(
|
|
565
|
+
gap_score, gap_open_score, gap_extend_score
|
|
566
|
+
)
|
|
567
|
+
if open_score == extend_score
|
|
568
|
+
return local ?
|
|
569
|
+
stride_smith_waterman(
|
|
570
|
+
query, target, match_score: match_score,
|
|
571
|
+
mismatch_score: mismatch_score, gap_score: open_score
|
|
572
|
+
) :
|
|
573
|
+
stride_needleman_wunsch(
|
|
574
|
+
query, target, match_score: match_score,
|
|
575
|
+
mismatch_score: mismatch_score, gap_score: open_score
|
|
576
|
+
)
|
|
577
|
+
end
|
|
578
|
+
local ?
|
|
579
|
+
stride_smith_waterman_affine(
|
|
580
|
+
query, target, match_score: match_score,
|
|
581
|
+
mismatch_score: mismatch_score, gap_open_score: open_score,
|
|
582
|
+
gap_extend_score: extend_score
|
|
583
|
+
) :
|
|
584
|
+
stride_needleman_wunsch_affine(
|
|
585
|
+
query, target, match_score: match_score,
|
|
586
|
+
mismatch_score: mismatch_score, gap_open_score: open_score,
|
|
587
|
+
gap_extend_score: extend_score
|
|
588
|
+
)
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def normalize_alignment_result(raw, query, target, local, match_score)
|
|
592
|
+
unless match_score.is_a?(Numeric) && match_score.finite? && match_score.positive?
|
|
593
|
+
raise ArgumentError, "match_score must be positive for normalized scores"
|
|
594
|
+
end
|
|
595
|
+
unless raw.is_a?(Array)
|
|
596
|
+
return normalize_one_alignment(raw, query, target, local, match_score)
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
left_values = query.is_a?(Array) ? query : [query]
|
|
600
|
+
right_values = target.is_a?(Array) ? target : [target]
|
|
601
|
+
raw.each_with_index.map do |score, index|
|
|
602
|
+
left = left_values[left_values.length == 1 ? 0 : index]
|
|
603
|
+
right = right_values[right_values.length == 1 ? 0 : index]
|
|
604
|
+
normalize_one_alignment(score, left, right, local, match_score)
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def normalize_one_alignment(raw, query, target, local, match_score)
|
|
609
|
+
return nil if raw.nil? || query.nil? || target.nil?
|
|
610
|
+
query_length = query.each_char.count
|
|
611
|
+
target_length = target.each_char.count
|
|
612
|
+
denominator = (local ? [query_length, target_length].min :
|
|
613
|
+
[query_length, target_length].max) * match_score
|
|
614
|
+
return 1.0 if query_length.zero? && target_length.zero?
|
|
615
|
+
return 0.0 if denominator <= 0
|
|
616
|
+
[[raw.to_f / denominator, 0.0].max, 1.0].min
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def one_to_many(query, targets)
|
|
620
|
+
query = scalar_query(query)
|
|
621
|
+
targets = target_array(targets)
|
|
622
|
+
targets.map { |target| yield(query, target) }
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
def scorer_table
|
|
626
|
+
@scorer_table ||= [
|
|
627
|
+
ScorerEntry.new(names: %w[levenshtein levenshtein_score], id: 0,
|
|
628
|
+
batch_method: :levenshtein_scores, higher: false, normalized: false),
|
|
629
|
+
ScorerEntry.new(names: %w[levenshtein_normalized levenshtein_normalized_score], id: 1,
|
|
630
|
+
batch_method: :levenshtein_normalized_scores, higher: true, normalized: true),
|
|
631
|
+
ScorerEntry.new(names: %w[damerau_levenshtein osa damerau_levenshtein_score], id: 2,
|
|
632
|
+
batch_method: :damerau_levenshtein_scores, higher: false, normalized: false),
|
|
633
|
+
ScorerEntry.new(names: %w[damerau_levenshtein_normalized osa_normalized], id: 3,
|
|
634
|
+
batch_method: :damerau_levenshtein_normalized_scores, higher: true, normalized: true),
|
|
635
|
+
ScorerEntry.new(names: %w[hamming hamming_score], id: 4,
|
|
636
|
+
batch_method: :hamming_scores, higher: false, normalized: false),
|
|
637
|
+
ScorerEntry.new(names: %w[hamming_normalized hamming_normalized_score], id: 5,
|
|
638
|
+
batch_method: :hamming_normalized_scores, higher: true, normalized: true),
|
|
639
|
+
ScorerEntry.new(names: %w[jaro jaro_similarity], id: 6,
|
|
640
|
+
batch_method: :jaro_similarities, higher: true, normalized: true),
|
|
641
|
+
ScorerEntry.new(names: %w[jaro_winkler jaro_winkler_similarity], id: 7,
|
|
642
|
+
batch_method: :jaro_winkler_similarities, higher: true, normalized: true),
|
|
643
|
+
ScorerEntry.new(names: %w[indel indel_score], id: 8,
|
|
644
|
+
batch_method: :indel_scores, higher: false, normalized: false),
|
|
645
|
+
ScorerEntry.new(names: %w[indel_normalized indel_normalized_score], id: 9,
|
|
646
|
+
batch_method: :indel_normalized_scores, higher: true, normalized: true),
|
|
647
|
+
ScorerEntry.new(names: %w[true_damerau_levenshtein true_damerau_levenshtein_score], id: 10,
|
|
648
|
+
batch_method: :true_damerau_levenshtein_scores, higher: false, normalized: false),
|
|
649
|
+
ScorerEntry.new(names: %w[true_damerau_levenshtein_normalized], id: 11,
|
|
650
|
+
batch_method: :true_damerau_levenshtein_normalized_scores, higher: true, normalized: true),
|
|
651
|
+
ScorerEntry.new(names: %w[sw smith_waterman smith-waterman local smith_waterman_score], id: 12,
|
|
652
|
+
batch_method: :smith_waterman_scores, higher: true, normalized: false),
|
|
653
|
+
ScorerEntry.new(names: %w[smith_waterman_normalized], id: 13,
|
|
654
|
+
batch_method: :smith_waterman_normalized_scores, higher: true, normalized: true),
|
|
655
|
+
ScorerEntry.new(names: %w[nw needleman_wunsch needleman-wunsch global needleman_wunsch_score], id: 14,
|
|
656
|
+
batch_method: :needleman_wunsch_scores, higher: true, normalized: false),
|
|
657
|
+
ScorerEntry.new(names: %w[needleman_wunsch_normalized], id: 15,
|
|
658
|
+
batch_method: :needleman_wunsch_normalized_scores, higher: true, normalized: true)
|
|
659
|
+
].freeze
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def resolve_scorer(scorer)
|
|
663
|
+
if scorer.respond_to?(:call)
|
|
664
|
+
name = scorer.respond_to?(:name) ? scorer.name.to_s : ""
|
|
665
|
+
entry = scorer_table.find { |candidate| candidate.batch_method.to_s == name }
|
|
666
|
+
return entry if entry
|
|
667
|
+
raise ArgumentError, "unknown stride-align scorer function"
|
|
668
|
+
end
|
|
669
|
+
entry = if scorer.is_a?(Numeric)
|
|
670
|
+
scorer_table.find { |candidate| candidate.id == scorer.to_i }
|
|
671
|
+
else
|
|
672
|
+
name = scorer.to_s.downcase
|
|
673
|
+
scorer_table.find { |candidate| candidate.names.include?(name) }
|
|
674
|
+
end
|
|
675
|
+
entry || raise(ArgumentError, "unknown stride-align scorer")
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
def ranked(targets, scores, k, higher)
|
|
679
|
+
k = non_negative_integer(k, "k")
|
|
680
|
+
values = targets.each_index.each_with_object([]) do |index, output|
|
|
681
|
+
next if scores[index].nil?
|
|
682
|
+
output << { target: targets[index], score: scores[index], index: index }
|
|
683
|
+
end
|
|
684
|
+
values.sort_by do |item|
|
|
685
|
+
[higher ? -item[:score] : item[:score], item[:index]]
|
|
686
|
+
end.first(k)
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def top_k(query, targets, scorer, k: 5, **kwargs)
|
|
690
|
+
query = scalar_query(query)
|
|
691
|
+
targets = target_array(targets)
|
|
692
|
+
entry = resolve_scorer(scorer)
|
|
693
|
+
scores = if kwargs.empty?
|
|
694
|
+
public_send(entry.batch_method, query, targets)
|
|
695
|
+
else
|
|
696
|
+
public_send(entry.batch_method, query, targets, **kwargs)
|
|
697
|
+
end
|
|
698
|
+
ranked(targets, scores, k, entry.higher)
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
def best(query, targets, scorer, **kwargs)
|
|
702
|
+
top_k(query, targets, scorer, k: 1, **kwargs).first
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
end
|