7#include <rapidfuzz/details/Range.hpp>
8#include <rapidfuzz/details/common.hpp>
9#include <rapidfuzz/details/simd.hpp>
15template <
typename T,
typename... Args>
16struct NormalizedMetricBase {
17 template <
typename InputIt1,
typename InputIt2,
18 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
19 static double normalized_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
20 Args... args,
double score_cutoff,
double score_hint)
22 return _normalized_distance(make_range(first1, last1), make_range(first2, last2),
23 std::forward<Args>(args)..., score_cutoff, score_hint);
26 template <
typename Sentence1,
typename Sentence2>
27 static double normalized_distance(
const Sentence1& s1,
const Sentence2& s2, Args... args,
28 double score_cutoff,
double score_hint)
30 return _normalized_distance(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
34 template <
typename InputIt1,
typename InputIt2,
35 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
36 static double normalized_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
37 Args... args,
double score_cutoff,
double score_hint)
39 return _normalized_similarity(make_range(first1, last1), make_range(first2, last2),
40 std::forward<Args>(args)..., score_cutoff, score_hint);
43 template <
typename Sentence1,
typename Sentence2>
44 static double normalized_similarity(
const Sentence1& s1,
const Sentence2& s2, Args... args,
45 double score_cutoff,
double score_hint)
47 return _normalized_similarity(make_range(s1), make_range(s2), std::forward<Args>(args)...,
48 score_cutoff, score_hint);
52 template <
typename InputIt1,
typename InputIt2>
53 static double _normalized_distance(
const Range<InputIt1>& s1,
const Range<InputIt2>& s2, Args... args,
54 double score_cutoff,
double score_hint)
56 auto maximum = T::maximum(s1, s2, args...);
57 auto cutoff_distance =
58 static_cast<decltype(maximum)
>(std::ceil(
static_cast<double>(maximum) * score_cutoff));
60 static_cast<decltype(maximum)
>(std::ceil(
static_cast<double>(maximum) * score_hint));
61 auto dist = T::_distance(s1, s2, std::forward<Args>(args)..., cutoff_distance, hint_distance);
62 double norm_dist = (maximum != 0) ?
static_cast<double>(dist) /
static_cast<double>(maximum) : 0.0;
63 return (norm_dist <= score_cutoff) ? norm_dist : 1.0;
66 template <
typename InputIt1,
typename InputIt2>
67 static double _normalized_similarity(
const Range<InputIt1>& s1,
const Range<InputIt2>& s2, Args... args,
68 double score_cutoff,
double score_hint)
70 double cutoff_score = NormSim_to_NormDist(score_cutoff);
71 double hint_score = NormSim_to_NormDist(score_hint);
73 _normalized_distance(s1, s2, std::forward<Args>(args)..., cutoff_score, hint_score);
74 double norm_sim = 1.0 - norm_dist;
75 return (norm_sim >= score_cutoff) ? norm_sim : 0.0;
78 NormalizedMetricBase()
83template <
typename T,
typename ResType, int64_t WorstSimilarity, int64_t WorstDistance,
typename... Args>
84struct DistanceBase :
public NormalizedMetricBase<T, Args...> {
85 template <
typename InputIt1,
typename InputIt2,
86 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
87 static ResType distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Args... args,
88 ResType score_cutoff, ResType score_hint)
90 return T::_distance(make_range(first1, last1), make_range(first2, last2), std::forward<Args>(args)...,
91 score_cutoff, score_hint);
94 template <
typename Sentence1,
typename Sentence2>
95 static ResType distance(
const Sentence1& s1,
const Sentence2& s2, Args... args, ResType score_cutoff,
98 return T::_distance(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
102 template <
typename InputIt1,
typename InputIt2,
103 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
104 static ResType similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Args... args,
105 ResType score_cutoff, ResType score_hint)
107 return _similarity(make_range(first1, last1), make_range(first2, last2), std::forward<Args>(args)...,
108 score_cutoff, score_hint);
111 template <
typename Sentence1,
typename Sentence2>
112 static ResType similarity(
const Sentence1& s1,
const Sentence2& s2, Args... args, ResType score_cutoff,
115 return _similarity(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
120 template <
typename InputIt1,
typename InputIt2>
121 static ResType _similarity(Range<InputIt1> s1, Range<InputIt2> s2, Args... args, ResType score_cutoff,
124 auto maximum = T::maximum(s1, s2, args...);
125 if (score_cutoff > maximum)
return 0;
127 score_hint = std::min(score_cutoff, score_hint);
128 ResType cutoff_distance = maximum - score_cutoff;
129 ResType hint_distance = maximum - score_hint;
130 ResType dist = T::_distance(s1, s2, std::forward<Args>(args)..., cutoff_distance, hint_distance);
131 ResType sim = maximum - dist;
132 return (sim >= score_cutoff) ? sim : 0;
140template <
typename T,
typename ResType, int64_t WorstSimilarity, int64_t WorstDistance,
typename... Args>
141struct SimilarityBase :
public NormalizedMetricBase<T, Args...> {
142 template <
typename InputIt1,
typename InputIt2,
143 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
144 static ResType distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Args... args,
145 ResType score_cutoff, ResType score_hint)
147 return _distance(make_range(first1, last1), make_range(first2, last2), std::forward<Args>(args)...,
148 score_cutoff, score_hint);
151 template <
typename Sentence1,
typename Sentence2>
152 static ResType distance(
const Sentence1& s1,
const Sentence2& s2, Args... args, ResType score_cutoff,
155 return _distance(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
159 template <
typename InputIt1,
typename InputIt2,
160 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
161 static ResType similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Args... args,
162 ResType score_cutoff, ResType score_hint)
164 return T::_similarity(make_range(first1, last1), make_range(first2, last2),
165 std::forward<Args>(args)..., score_cutoff, score_hint);
168 template <
typename Sentence1,
typename Sentence2>
169 static ResType similarity(
const Sentence1& s1,
const Sentence2& s2, Args... args, ResType score_cutoff,
172 return T::_similarity(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
177 template <
typename InputIt1,
typename InputIt2>
178 static ResType _distance(
const Range<InputIt1>& s1,
const Range<InputIt2>& s2, Args... args,
179 ResType score_cutoff, ResType score_hint)
181 auto maximum = T::maximum(s1, s2, args...);
182 ResType cutoff_similarity =
183 (maximum >= score_cutoff) ? maximum - score_cutoff : static_cast<ResType>(WorstSimilarity);
184 ResType hint_similarity =
185 (maximum >= score_hint) ? maximum - score_hint : static_cast<ResType>(WorstSimilarity);
186 ResType sim = T::_similarity(s1, s2, std::forward<Args>(args)..., cutoff_similarity, hint_similarity);
187 ResType dist = maximum - sim;
188 return _apply_distance_score_cutoff(dist, score_cutoff);
191 template <
typename U>
192 static rapidfuzz::rf_enable_if_t<std::is_floating_point<U>::value, U>
193 _apply_distance_score_cutoff(U score, U score_cutoff)
195 return (score <= score_cutoff) ? score : 1.0;
198 template <
typename U>
199 static rapidfuzz::rf_enable_if_t<!std::is_floating_point<U>::value, U>
200 _apply_distance_score_cutoff(U score, U score_cutoff)
202 return (score <= score_cutoff) ? score : score_cutoff + 1;
211struct CachedNormalizedMetricBase {
212 template <
typename InputIt2>
213 double normalized_distance(InputIt2 first2, InputIt2 last2,
double score_cutoff = 1.0,
214 double score_hint = 1.0)
const
216 return _normalized_distance(make_range(first2, last2), score_cutoff, score_hint);
219 template <
typename Sentence2>
220 double normalized_distance(
const Sentence2& s2,
double score_cutoff = 1.0,
double score_hint = 1.0)
const
222 return _normalized_distance(make_range(s2), score_cutoff, score_hint);
225 template <
typename InputIt2>
226 double normalized_similarity(InputIt2 first2, InputIt2 last2,
double score_cutoff = 0.0,
227 double score_hint = 0.0)
const
229 return _normalized_similarity(make_range(first2, last2), score_cutoff, score_hint);
232 template <
typename Sentence2>
233 double normalized_similarity(
const Sentence2& s2,
double score_cutoff = 0.0,
234 double score_hint = 0.0)
const
236 return _normalized_similarity(make_range(s2), score_cutoff, score_hint);
240 template <
typename InputIt2>
241 double _normalized_distance(
const Range<InputIt2>& s2,
double score_cutoff,
double score_hint)
const
243 const T& derived =
static_cast<const T&
>(*this);
244 auto maximum = derived.maximum(s2);
245 auto cutoff_distance =
246 static_cast<decltype(maximum)
>(std::ceil(
static_cast<double>(maximum) * score_cutoff));
248 static_cast<decltype(maximum)
>(std::ceil(
static_cast<double>(maximum) * score_hint));
249 double dist =
static_cast<double>(derived._distance(s2, cutoff_distance, hint_distance));
250 double norm_dist = (maximum != 0) ? dist /
static_cast<double>(maximum) : 0.0;
251 return (norm_dist <= score_cutoff) ? norm_dist : 1.0;
254 template <
typename InputIt2>
255 double _normalized_similarity(
const Range<InputIt2>& s2,
double score_cutoff,
double score_hint)
const
257 double cutoff_score = NormSim_to_NormDist(score_cutoff);
258 double hint_score = NormSim_to_NormDist(score_hint);
259 double norm_dist = _normalized_distance(s2, cutoff_score, hint_score);
260 double norm_sim = 1.0 - norm_dist;
261 return (norm_sim >= score_cutoff) ? norm_sim : 0.0;
264 CachedNormalizedMetricBase()
269template <
typename T,
typename ResType,
int64_t WorstSimilarity,
int64_t WorstDistance>
270struct CachedDistanceBase :
public CachedNormalizedMetricBase<T> {
271 template <
typename InputIt2>
272 ResType distance(InputIt2 first2, InputIt2 last2,
273 ResType score_cutoff =
static_cast<ResType
>(WorstDistance),
274 ResType score_hint =
static_cast<ResType
>(WorstDistance))
const
276 const T& derived =
static_cast<const T&
>(*this);
277 return derived._distance(make_range(first2, last2), score_cutoff, score_hint);
280 template <
typename Sentence2>
281 ResType distance(
const Sentence2& s2, ResType score_cutoff =
static_cast<ResType
>(WorstDistance),
282 ResType score_hint =
static_cast<ResType
>(WorstDistance))
const
284 const T& derived =
static_cast<const T&
>(*this);
285 return derived._distance(make_range(s2), score_cutoff, score_hint);
288 template <
typename InputIt2>
289 ResType similarity(InputIt2 first2, InputIt2 last2,
290 ResType score_cutoff =
static_cast<ResType
>(WorstSimilarity),
291 ResType score_hint =
static_cast<ResType
>(WorstSimilarity))
const
293 return _similarity(make_range(first2, last2), score_cutoff, score_hint);
296 template <
typename Sentence2>
297 ResType similarity(
const Sentence2& s2, ResType score_cutoff =
static_cast<ResType
>(WorstSimilarity),
298 ResType score_hint =
static_cast<ResType
>(WorstSimilarity))
const
300 return _similarity(make_range(s2), score_cutoff, score_hint);
304 template <
typename InputIt2>
305 ResType _similarity(
const Range<InputIt2>& s2, ResType score_cutoff, ResType score_hint)
const
307 const T& derived =
static_cast<const T&
>(*this);
308 ResType maximum = derived.maximum(s2);
309 if (score_cutoff > maximum)
return 0;
311 score_hint = std::min(score_cutoff, score_hint);
312 ResType cutoff_distance = maximum - score_cutoff;
313 ResType hint_distance = maximum - score_hint;
314 ResType dist = derived._distance(s2, cutoff_distance, hint_distance);
315 ResType sim = maximum - dist;
316 return (sim >= score_cutoff) ? sim : 0;
324template <
typename T,
typename ResType,
int64_t WorstSimilarity,
int64_t WorstDistance>
325struct CachedSimilarityBase :
public CachedNormalizedMetricBase<T> {
326 template <
typename InputIt2>
327 ResType distance(InputIt2 first2, InputIt2 last2,
328 ResType score_cutoff =
static_cast<ResType
>(WorstDistance),
329 ResType score_hint =
static_cast<ResType
>(WorstDistance))
const
331 return _distance(make_range(first2, last2), score_cutoff, score_hint);
334 template <
typename Sentence2>
335 ResType distance(
const Sentence2& s2, ResType score_cutoff =
static_cast<ResType
>(WorstDistance),
336 ResType score_hint =
static_cast<ResType
>(WorstDistance))
const
338 return _distance(make_range(s2), score_cutoff, score_hint);
341 template <
typename InputIt2>
342 ResType similarity(InputIt2 first2, InputIt2 last2,
343 ResType score_cutoff =
static_cast<ResType
>(WorstSimilarity),
344 ResType score_hint =
static_cast<ResType
>(WorstSimilarity))
const
346 const T& derived =
static_cast<const T&
>(*this);
347 return derived._similarity(make_range(first2, last2), score_cutoff, score_hint);
350 template <
typename Sentence2>
351 ResType similarity(
const Sentence2& s2, ResType score_cutoff =
static_cast<ResType
>(WorstSimilarity),
352 ResType score_hint =
static_cast<ResType
>(WorstSimilarity))
const
354 const T& derived =
static_cast<const T&
>(*this);
355 return derived._similarity(make_range(s2), score_cutoff, score_hint);
359 template <
typename InputIt2>
360 ResType _distance(
const Range<InputIt2>& s2, ResType score_cutoff, ResType score_hint)
const
362 const T& derived =
static_cast<const T&
>(*this);
363 ResType maximum = derived.maximum(s2);
364 ResType cutoff_similarity = (maximum > score_cutoff) ? maximum - score_cutoff : 0;
365 ResType hint_similarity = (maximum > score_hint) ? maximum - score_hint : 0;
366 ResType sim = derived._similarity(s2, cutoff_similarity, hint_similarity);
367 ResType dist = maximum - sim;
368 return _apply_distance_score_cutoff(dist, score_cutoff);
371 template <
typename U>
372 static rapidfuzz::rf_enable_if_t<std::is_floating_point<U>::value, U>
373 _apply_distance_score_cutoff(U score, U score_cutoff)
375 return (score <= score_cutoff) ? score : 1.0;
378 template <
typename U>
379 static rapidfuzz::rf_enable_if_t<!std::is_floating_point<U>::value, U>
380 _apply_distance_score_cutoff(U score, U score_cutoff)
382 return (score <= score_cutoff) ? score : score_cutoff + 1;
385 CachedSimilarityBase()
390template <
typename T,
typename ResType>
391struct MultiNormalizedMetricBase {
392 template <
typename InputIt2>
393 void normalized_distance(
double* scores,
size_t score_count, InputIt2 first2, InputIt2 last2,
394 double score_cutoff = 1.0)
const
396 _normalized_distance(scores, score_count, make_range(first2, last2), score_cutoff);
399 template <
typename Sentence2>
400 void normalized_distance(
double* scores,
size_t score_count,
const Sentence2& s2,
401 double score_cutoff = 1.0)
const
403 _normalized_distance(scores, score_count, make_range(s2), score_cutoff);
406 template <
typename InputIt2>
407 void normalized_similarity(
double* scores,
size_t score_count, InputIt2 first2, InputIt2 last2,
408 double score_cutoff = 0.0)
const
410 _normalized_similarity(scores, score_count, make_range(first2, last2), score_cutoff);
413 template <
typename Sentence2>
414 void normalized_similarity(
double* scores,
size_t score_count,
const Sentence2& s2,
415 double score_cutoff = 0.0)
const
417 _normalized_similarity(scores, score_count, make_range(s2), score_cutoff);
421 template <
typename InputIt2>
422 void _normalized_distance(
double* scores,
size_t score_count,
const Range<InputIt2>& s2,
423 double score_cutoff = 1.0)
const
425 const T& derived =
static_cast<const T&
>(*this);
426 if (score_count < derived.result_count())
427 throw std::invalid_argument(
"scores has to have >= result_count() elements");
430 ResType* scores_orig =
nullptr;
432 RAPIDFUZZ_IF_CONSTEXPR (
sizeof(
double) ==
sizeof(ResType))
433 scores_orig =
reinterpret_cast<ResType*
>(scores);
435 scores_orig =
new ResType[derived.result_count()];
437 derived.distance(scores_orig, derived.result_count(), s2);
439 for (
size_t i = 0; i < derived.get_input_count(); ++i) {
440 auto maximum = derived.maximum(i, s2);
442 (maximum != 0) ?
static_cast<double>(scores_orig[i]) /
static_cast<double>(maximum) : 0.0;
443 scores[i] = (norm_dist <= score_cutoff) ? norm_dist : 1.0;
446 RAPIDFUZZ_IF_CONSTEXPR (
sizeof(
double) !=
sizeof(ResType))
delete[] scores_orig;
449 template <
typename InputIt2>
450 void _normalized_similarity(
double* scores,
size_t score_count,
const Range<InputIt2>& s2,
451 double score_cutoff)
const
453 const T& derived =
static_cast<const T&
>(*this);
454 _normalized_distance(scores, score_count, s2);
456 for (
size_t i = 0; i < derived.get_input_count(); ++i) {
457 double norm_sim = 1.0 - scores[i];
458 scores[i] = (norm_sim >= score_cutoff) ? norm_sim : 0.0;
462 MultiNormalizedMetricBase()
467template <
typename T,
typename ResType,
int64_t WorstSimilarity,
int64_t WorstDistance>
468struct MultiDistanceBase :
public MultiNormalizedMetricBase<T, ResType> {
469 template <
typename InputIt2>
470 void distance(ResType* scores,
size_t score_count, InputIt2 first2, InputIt2 last2,
471 ResType score_cutoff =
static_cast<ResType
>(WorstDistance))
const
473 const T& derived =
static_cast<const T&
>(*this);
474 derived._distance(scores, score_count, make_range(first2, last2), score_cutoff);
477 template <
typename Sentence2>
478 void distance(ResType* scores,
size_t score_count,
const Sentence2& s2,
479 ResType score_cutoff =
static_cast<ResType
>(WorstDistance))
const
481 const T& derived =
static_cast<const T&
>(*this);
482 derived._distance(scores, score_count, make_range(s2), score_cutoff);
485 template <
typename InputIt2>
486 void similarity(ResType* scores,
size_t score_count, InputIt2 first2, InputIt2 last2,
487 ResType score_cutoff =
static_cast<ResType
>(WorstSimilarity))
const
489 _similarity(scores, score_count, make_range(first2, last2), score_cutoff);
492 template <
typename Sentence2>
493 void similarity(ResType* scores,
size_t score_count,
const Sentence2& s2,
494 ResType score_cutoff =
static_cast<ResType
>(WorstSimilarity))
const
496 _similarity(scores, score_count, make_range(s2), score_cutoff);
500 template <
typename InputIt2>
501 void _similarity(ResType* scores,
size_t score_count,
const Range<InputIt2>& s2,
502 ResType score_cutoff)
const
504 const T& derived =
static_cast<const T&
>(*this);
505 derived._distance(scores, score_count, s2);
507 for (
size_t i = 0; i < derived.get_input_count(); ++i) {
508 ResType maximum = derived.maximum(i, s2);
509 ResType sim = maximum - scores[i];
510 scores[i] = (sim >= score_cutoff) ? sim : 0;
519template <
typename T,
typename ResType,
int64_t WorstSimilarity,
int64_t WorstDistance>
520struct MultiSimilarityBase :
public MultiNormalizedMetricBase<T, ResType> {
521 template <
typename InputIt2>
522 void distance(ResType* scores,
size_t score_count, InputIt2 first2, InputIt2 last2,
523 ResType score_cutoff =
static_cast<ResType
>(WorstDistance))
const
525 _distance(scores, score_count, make_range(first2, last2), score_cutoff);
528 template <
typename Sentence2>
529 void distance(ResType* scores,
size_t score_count,
const Sentence2& s2,
530 ResType score_cutoff =
static_cast<ResType
>(WorstDistance))
const
532 _distance(scores, score_count, make_range(s2), score_cutoff);
535 template <
typename InputIt2>
536 void similarity(ResType* scores,
size_t score_count, InputIt2 first2, InputIt2 last2,
537 ResType score_cutoff =
static_cast<ResType
>(WorstSimilarity))
const
539 const T& derived =
static_cast<const T&
>(*this);
540 derived._similarity(scores, score_count, make_range(first2, last2), score_cutoff);
543 template <
typename Sentence2>
544 void similarity(ResType* scores,
size_t score_count,
const Sentence2& s2,
545 ResType score_cutoff =
static_cast<ResType
>(WorstSimilarity))
const
547 const T& derived =
static_cast<const T&
>(*this);
548 derived._similarity(scores, score_count, make_range(s2), score_cutoff);
552 template <
typename InputIt2>
553 void _distance(ResType* scores,
size_t score_count,
const Range<InputIt2>& s2, ResType score_cutoff)
const
555 const T& derived =
static_cast<const T&
>(*this);
556 derived._similarity(scores, score_count, s2);
558 for (
size_t i = 0; i < derived.get_input_count(); ++i) {
559 ResType maximum = derived.maximum(i, s2);
560 ResType dist = maximum - scores[i];
561 scores[i] = _apply_distance_score_cutoff(dist, score_cutoff);
565 template <
typename U>
566 static rapidfuzz::rf_enable_if_t<std::is_floating_point<U>::value, U>
567 _apply_distance_score_cutoff(U score, U score_cutoff)
569 return (score <= score_cutoff) ? score : 1.0;
572 template <
typename U>
573 static rapidfuzz::rf_enable_if_t<!std::is_floating_point<U>::value, U>
574 _apply_distance_score_cutoff(U score, U score_cutoff)
576 return (score <= score_cutoff) ? score : score_cutoff + 1;
579 MultiSimilarityBase()