use std::collections::HashMap;
use super::super::hash::bm25_hash;
use super::super::stemmer::stem;
use super::super::stopwords::Stopwords;
use super::super::words;
use crate::sparse::SparseEmbedding;
const ALPHA: f32 = 0.5;
const SPECIAL_TOKENS: [&str; 5] = ["[CLS]", "[SEP]", "[PAD]", "[UNK]", "[MASK]"];
fn is_special_token(word: &str) -> bool {
SPECIAL_TOKENS.contains(&word)
}
const ASCII_PUNCTUATION: &str = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
fn is_ascii_punctuation(word: &str) -> bool {
let mut chars = word.chars();
match (chars.next(), chars.next()) {
(Some(only), None) => ASCII_PUNCTUATION.contains(only),
_ => false,
}
}
fn is_dropped(word: &str, stopwords: &Stopwords) -> bool {
is_special_token(word) || stopwords.contains(word) || is_ascii_punctuation(word)
}
pub(super) fn pool_row(
pooled: &[f32],
ids: &[u32],
token_text: &[String],
stopwords: &Stopwords,
) -> SparseEmbedding {
let texts: Vec<String> = ids
.iter()
.map(|&id| token_text.get(id as usize).cloned().unwrap_or_default())
.collect();
let mut max_weight: HashMap<String, f32> = HashMap::new();
for word in words::reconstruct(&texts) {
if is_dropped(&word.text, stopwords) {
continue;
}
let weight: f32 = word.tokens.clone().map(|position| pooled[position]).sum();
let stemmed = stem(&word.text);
let entry = max_weight.entry(stemmed).or_insert(0.0);
if weight > *entry {
*entry = weight;
}
}
SparseEmbedding::from_pairs(
max_weight
.into_iter()
.map(|(stemmed, weight)| (bm25_hash(&stemmed), (1.0 + weight).ln().powf(ALPHA))),
)
}
pub(super) fn pool_query_row(
ids: &[u32],
token_text: &[String],
stopwords: &Stopwords,
) -> SparseEmbedding {
let texts: Vec<String> = ids
.iter()
.map(|&id| token_text.get(id as usize).cloned().unwrap_or_default())
.collect();
let pairs = words::reconstruct(&texts)
.into_iter()
.filter(|word| !is_dropped(&word.text, stopwords))
.map(|word| (bm25_hash(&stem(&word.text)), 1.0f32));
SparseEmbedding::from_pairs(pairs)
}
#[cfg(test)]
mod tests;