RapidFuzz
Loading...
Searching...
No Matches
OSA.hpp
1/* SPDX-License-Identifier: MIT */
2/* Copyright © 2021 Max Bachmann */
3
4#pragma once
5
6#include <limits>
7#include <rapidfuzz/details/common.hpp>
8#include <rapidfuzz/distance/OSA_impl.hpp>
9
10namespace rapidfuzz {
11
41template <typename InputIt1, typename InputIt2>
42size_t osa_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
43 size_t score_cutoff = std::numeric_limits<size_t>::max())
44{
45 return detail::OSA::distance(first1, last1, first2, last2, score_cutoff, score_cutoff);
46}
47
48template <typename Sentence1, typename Sentence2>
49size_t osa_distance(const Sentence1& s1, const Sentence2& s2,
50 size_t score_cutoff = std::numeric_limits<size_t>::max())
51{
52 return detail::OSA::distance(s1, s2, score_cutoff, score_cutoff);
53}
54
55template <typename InputIt1, typename InputIt2>
56size_t osa_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
57 size_t score_cutoff = 0)
58{
59 return detail::OSA::similarity(first1, last1, first2, last2, score_cutoff, score_cutoff);
60}
61
62template <typename Sentence1, typename Sentence2>
63size_t osa_similarity(const Sentence1& s1, const Sentence2& s2, size_t score_cutoff = 0)
64{
65 return detail::OSA::similarity(s1, s2, score_cutoff, score_cutoff);
66}
67
68template <typename InputIt1, typename InputIt2>
69double osa_normalized_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
70 double score_cutoff = 1.0)
71{
72 return detail::OSA::normalized_distance(first1, last1, first2, last2, score_cutoff, score_cutoff);
73}
74
75template <typename Sentence1, typename Sentence2>
76double osa_normalized_distance(const Sentence1& s1, const Sentence2& s2, double score_cutoff = 1.0)
77{
78 return detail::OSA::normalized_distance(s1, s2, score_cutoff, score_cutoff);
79}
80
105template <typename InputIt1, typename InputIt2>
106double osa_normalized_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
107 double score_cutoff = 0.0)
108{
109 return detail::OSA::normalized_similarity(first1, last1, first2, last2, score_cutoff, score_cutoff);
110}
111
112template <typename Sentence1, typename Sentence2>
113double osa_normalized_similarity(const Sentence1& s1, const Sentence2& s2, double score_cutoff = 0.0)
114{
115 return detail::OSA::normalized_similarity(s1, s2, score_cutoff, score_cutoff);
116}
117
118#ifdef RAPIDFUZZ_SIMD
119namespace experimental {
120template <int MaxLen>
121struct MultiOSA
122 : public detail::MultiDistanceBase<MultiOSA<MaxLen>, size_t, 0, std::numeric_limits<int64_t>::max()> {
123private:
124 friend detail::MultiDistanceBase<MultiOSA<MaxLen>, size_t, 0, std::numeric_limits<int64_t>::max()>;
125 friend detail::MultiNormalizedMetricBase<MultiOSA<MaxLen>, size_t>;
126
127 RAPIDFUZZ_CONSTEXPR_CXX14 static size_t get_vec_size()
128 {
129# ifdef RAPIDFUZZ_AVX2
130 using namespace detail::simd_avx2;
131# else
132 using namespace detail::simd_sse2;
133# endif
134 RAPIDFUZZ_IF_CONSTEXPR (MaxLen <= 8)
135 return native_simd<uint8_t>::size;
136 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen <= 16)
137 return native_simd<uint16_t>::size;
138 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen <= 32)
139 return native_simd<uint32_t>::size;
140 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen <= 64)
141 return native_simd<uint64_t>::size;
142
143 static_assert(MaxLen <= 64, "expected MaxLen <= 64");
144 }
145
146 static size_t find_block_count(size_t count)
147 {
148 size_t vec_size = get_vec_size();
149 size_t simd_vec_count = detail::ceil_div(count, vec_size);
150 return detail::ceil_div(simd_vec_count * vec_size * MaxLen, 64);
151 }
152
153public:
154 MultiOSA(size_t count) : input_count(count), PM(find_block_count(count) * 64)
155 {
156 str_lens.resize(result_count());
157 }
158
168 size_t result_count() const
169 {
170 size_t vec_size = get_vec_size();
171 size_t simd_vec_count = detail::ceil_div(input_count, vec_size);
172 return simd_vec_count * vec_size;
173 }
174
175 template <typename Sentence1>
176 void insert(const Sentence1& s1_)
177 {
178 insert(detail::to_begin(s1_), detail::to_end(s1_));
179 }
180
181 template <typename InputIt1>
182 void insert(InputIt1 first1, InputIt1 last1)
183 {
184 auto len = std::distance(first1, last1);
185 int block_pos = static_cast<int>((pos * MaxLen) % 64);
186 auto block = (pos * MaxLen) / 64;
187 assert(len <= MaxLen);
188
189 if (pos >= input_count) throw std::invalid_argument("out of bounds insert");
190
191 str_lens[pos] = static_cast<size_t>(len);
192 for (; first1 != last1; ++first1) {
193 PM.insert(block, *first1, block_pos);
194 block_pos++;
195 }
196 pos++;
197 }
198
199private:
200 template <typename InputIt2>
201 void _distance(size_t* scores, size_t score_count, const detail::Range<InputIt2>& s2,
202 size_t score_cutoff = std::numeric_limits<size_t>::max()) const
203 {
204 if (score_count < result_count())
205 throw std::invalid_argument("scores has to have >= result_count() elements");
206
207 auto scores_ = detail::make_range(scores, scores + score_count);
208 RAPIDFUZZ_IF_CONSTEXPR (MaxLen == 8)
209 detail::osa_hyrroe2003_simd<uint8_t>(scores_, PM, str_lens, s2, score_cutoff);
210 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen == 16)
211 detail::osa_hyrroe2003_simd<uint16_t>(scores_, PM, str_lens, s2, score_cutoff);
212 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen == 32)
213 detail::osa_hyrroe2003_simd<uint32_t>(scores_, PM, str_lens, s2, score_cutoff);
214 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen == 64)
215 detail::osa_hyrroe2003_simd<uint64_t>(scores_, PM, str_lens, s2, score_cutoff);
216 }
217
218 template <typename InputIt2>
219 size_t maximum(size_t s1_idx, const detail::Range<InputIt2>& s2) const
220 {
221 return std::max(str_lens[s1_idx], s2.size());
222 }
223
224 size_t get_input_count() const noexcept
225 {
226 return input_count;
227 }
228
229 size_t input_count;
230 size_t pos = 0;
231 detail::BlockPatternMatchVector PM;
232 std::vector<size_t> str_lens;
233};
234} /* namespace experimental */
235#endif
236
237template <typename CharT1>
238struct CachedOSA
239 : public detail::CachedDistanceBase<CachedOSA<CharT1>, size_t, 0, std::numeric_limits<int64_t>::max()> {
240 template <typename Sentence1>
241 explicit CachedOSA(const Sentence1& s1_) : CachedOSA(detail::to_begin(s1_), detail::to_end(s1_))
242 {}
243
244 template <typename InputIt1>
245 CachedOSA(InputIt1 first1, InputIt1 last1) : s1(first1, last1), PM(detail::make_range(first1, last1))
246 {}
247
248private:
249 friend detail::CachedDistanceBase<CachedOSA<CharT1>, size_t, 0, std::numeric_limits<int64_t>::max()>;
250 friend detail::CachedNormalizedMetricBase<CachedOSA<CharT1>>;
251
252 template <typename InputIt2>
253 size_t maximum(const detail::Range<InputIt2>& s2) const
254 {
255 return std::max(s1.size(), s2.size());
256 }
257
258 template <typename InputIt2>
259 size_t _distance(const detail::Range<InputIt2>& s2, size_t score_cutoff, size_t) const
260 {
261 size_t res;
262 if (s1.empty())
263 res = s2.size();
264 else if (s2.empty())
265 res = s1.size();
266 else if (s1.size() < 64)
267 res = detail::osa_hyrroe2003(PM, detail::make_range(s1), s2, score_cutoff);
268 else
269 res = detail::osa_hyrroe2003_block(PM, detail::make_range(s1), s2, score_cutoff);
270
271 return (res <= score_cutoff) ? res : score_cutoff + 1;
272 }
273
274 std::vector<CharT1> s1;
275 detail::BlockPatternMatchVector PM;
276};
277
278#ifdef RAPIDFUZZ_DEDUCTION_GUIDES
279template <typename Sentence1>
280CachedOSA(const Sentence1& s1_) -> CachedOSA<char_type<Sentence1>>;
281
282template <typename InputIt1>
283CachedOSA(InputIt1 first1, InputIt1 last1) -> CachedOSA<iter_value_t<InputIt1>>;
284#endif
285
288} // namespace rapidfuzz
size_t osa_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, size_t score_cutoff=std::numeric_limits< size_t >::max())
Calculates the optimal string alignment (OSA) distance between two strings.
Definition OSA.hpp:42
double osa_normalized_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, double score_cutoff=0.0)
Calculates a normalized hamming similarity.
Definition OSA.hpp:106