use std::collections::HashMap;
use candle_core::Tensor;
use super::index;
use super::term_frequency;
use crate::error::Result;
const NORM_FLOOR: f32 = 1e-8;
pub(super) struct RecognizedGroup {
token_sum: Vec<f32>,
token_count: u32,
pub(super) word_count: u32,
}
impl RecognizedGroup {
pub(super) fn new(hidden_size: usize) -> Self {
Self {
token_sum: vec![0.0; hidden_size],
token_count: 0,
word_count: 0,
}
}
pub(super) fn add_token(&mut self, hidden: &[f32]) {
for (sum, value) in self.token_sum.iter_mut().zip(hidden) {
*sum += value;
}
self.token_count += 1;
}
fn average(&self) -> Vec<f32> {
let count = self.token_count as f32;
self.token_sum.iter().map(|sum| sum / count).collect()
}
}
fn l2_normalize(x: &Tensor) -> Result<Tensor> {
let floor = Tensor::new(&[NORM_FLOOR], x.device())?;
let norm = x.sqr()?.sum_keepdim(1)?.sqrt()?.broadcast_maximum(&floor)?;
Ok(x.broadcast_div(&norm)?)
}
pub(super) fn pairs(
groups: &HashMap<u32, RecognizedGroup>,
matrix: &Tensor,
doc_len: u32,
uniform_weight: bool,
) -> Result<Vec<(u32, f32)>> {
if groups.is_empty() {
return Ok(Vec::new());
}
let device = matrix.device();
let hidden_size = matrix.dims()[1];
let word_ids: Vec<u32> = groups.keys().copied().collect();
let mut flat_avg = Vec::with_capacity(word_ids.len() * hidden_size);
for &word_id in &word_ids {
flat_avg.extend(groups[&word_id].average());
}
let ids = Tensor::from_vec(word_ids.clone(), word_ids.len(), device)?;
let avg = Tensor::from_vec(flat_avg, (word_ids.len(), hidden_size), device)?;
let gathered = matrix.index_select(&ids, 0)?;
let projected = avg.unsqueeze(1)?.matmul(&gathered)?.squeeze(1)?;
let normalized = l2_normalize(&projected.tanh()?)?;
let values = normalized.to_vec2::<f32>()?;
let mut pairs = Vec::with_capacity(word_ids.len() * index::EMBEDDING_SIZE as usize);
for (row, &word_id) in values.iter().zip(&word_ids) {
let tf = if uniform_weight {
1.0
} else {
term_frequency::saturation(groups[&word_id].word_count, doc_len)
};
for (component, &value) in row.iter().enumerate() {
pairs.push((index::dense_index(word_id, component as u32), value * tf));
}
}
Ok(pairs)
}