Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v.
2.
0. If a copy of the MPL was not distributed with this
* file, You can obtain one at
https://mozilla.org/MPL/2.
0/. */
use std::cmp::max;
use crate::interest::{Interest, InterestVector};
/// Calculate score for a piece of categorized content based on a user interest vector.
///
/// This scoring function is of the following properties:
/// - The score ranges from
0.
0 to
1.
0
/// - The score is monotonically increasing for the accumulated interest count
///
/// # Params:
/// - `interest_vector`: a user interest vector that can be fetched via
/// `RelevancyStore::user_interest_vector()`.
/// - `content_categories`: a list of categories (interests) of the give content.
/// # Return:
/// - A score ranges in [
0,
1].
#[uniffi::export]
pub fn score(interest_vector: InterestVector, content_categories: Vec<Interest>) -> f64 {
let n = content_categories
.iter()
.fold(
0, |acc, &category| acc + interest_vector[category]);
// Apply base
10 logarithm to the accumulated count so its hyperbolic tangent is more
// evenly distributed in [
0,
1]. Note that `max(n,
1)` is used to avoid negative scores.
(max(n,
1) as f64).log10().tanh()
}
#[cfg(test)]
mod test {
use crate::interest::{Interest, InterestVector};
use super::*;
const EPSILON: f64 =
1e-
10;
const SUBEPSILON: f64 =
1e-
6;
#[test]
fn test_score_lower_bound() {
// Empty interest vector yields score
0.
let s = score(InterestVector::default(), vec![Interest::Food]);
let delta = (s -
0_f64).abs();
assert!(delta < EPSILON);
// No overlap also yields score
0.
let s = score(
InterestVector {
animals:
10,
..InterestVector::default()
},
vec![Interest::Food],
);
let delta = (s -
0_f64).abs();
assert!(delta < EPSILON);
}
#[test]
fn test_score_upper_bound() {
let score = score(
InterestVector {
animals:
1_
000_
000_
000,
..InterestVector::default()
},
vec![Interest::Animals],
);
let delta = (score -
1.
0_f64).abs();
// Can get very close to the upper bound
1.
0 but not over.
assert!(delta < SUBEPSILON);
}
#[test]
fn test_score_monotonic() {
let l = score(
InterestVector {
animals:
1,
..InterestVector::default()
},
vec![Interest::Animals],
);
let r = score(
InterestVector {
animals:
5,
..InterestVector::default()
},
vec![Interest::Animals],
);
assert!(l < r);
}
#[test]
fn test_score_multi_categories() {
let l = score(
InterestVector {
animals:
100,
food:
100,
..InterestVector::default()
},
vec![Interest::Animals, Interest::Food],
);
let r = score(
InterestVector {
animals:
200,
..InterestVector::default()
},
vec![Interest::Animals],
);
let delta = (l - r).abs();
assert!(delta < EPSILON);
}
}