use rten_simd::ops::{BitOps, MaskOps, NumOps};
use rten_simd::{Isa, Simd, SimdIterable, SimdOp};
use rten_vecmath::Softmax;
use crate::Logits;
use crate::generator::TokenId;
pub trait LogitsFilter {
fn filter(&self, logits: Logits, prev_tokens: &[TokenId]) -> Logits;
}
struct TokenIdFilter<F: Fn(TokenId) -> bool> {
predicate: F,
}
impl<F: Fn(TokenId) -> bool> LogitsFilter for TokenIdFilter<F> {
fn filter(&self, logits: Logits, _prev_tokens: &[TokenId]) -> Logits {
let (logits, indices) = logits.into_logits_indices();
let (new_logits, new_indices) = logits
.into_iter()
.zip(indices)
.filter(|(_logit, token_id)| (self.predicate)(*token_id))
.unzip();
Logits::sparse(new_logits, new_indices)
}
}
pub fn token_id_filter<F: Fn(TokenId) -> bool>(predicate: F) -> impl LogitsFilter {
TokenIdFilter { predicate }
}
pub struct Temperature {
temperature: f32,
}
impl Temperature {
pub fn new(temperature: f32) -> Self {
assert!(temperature >= 0.);
Self { temperature }
}
}
impl LogitsFilter for Temperature {
fn filter(&self, logits: Logits, _prev_tokens: &[TokenId]) -> Logits {
if self.temperature == 1.0 {
return logits;
}
let (mut logits, indices) = logits.into_logits_indices();
let inv_temp = 1. / self.temperature;
for x in &mut logits {
*x *= inv_temp;
}
Logits::sparse(logits, indices)
}
}
pub struct Chain {
filters: Vec<Box<dyn LogitsFilter>>,
}
impl Default for Chain {
fn default() -> Self {
Self::new()
}
}
impl Chain {
pub fn new() -> Self {
Self {
filters: Vec::new(),
}
}
pub fn append<F: LogitsFilter + 'static>(mut self, filter: F) -> Self {
self.filters.push(Box::new(filter));
self
}
pub fn temperature(self, temp: f32) -> Self {
self.append(Temperature::new(temp))
}
pub fn top_p(self, p: f32) -> Self {
self.append(TopP::new(p))
}
pub fn top_k(self, k: usize) -> Self {
self.append(TopK::new(k))
}
}
impl LogitsFilter for Chain {
fn filter(&self, logits: Logits, prev_tokens: &[TokenId]) -> Logits {
self.filters
.iter()
.fold(logits, |logits, f| f.filter(logits, prev_tokens))
}
}
pub struct TopK {
k: usize,
}
impl TopK {
pub fn new(k: usize) -> Self {
Self { k }
}
}
impl LogitsFilter for TopK {
fn filter(&self, logits: Logits, _prev_tokens: &[TokenId]) -> Logits {
if logits.is_empty() {
return logits;
}
let (logits, indices) = logits.into_logits_indices();
let topk = SimdTopK {
k: self.k,
indices: &indices,
logits: &logits,
}
.dispatch();
let (indices, logits) = topk.into_iter().unzip();
Logits::sparse(logits, indices)
}
}
struct SimdTopK<'a> {
k: usize,
logits: &'a [f32],
indices: &'a [u32],
}
impl<'a> SimdOp for SimdTopK<'a> {
type Output = Vec<(u32, f32)>;
#[inline(always)]
fn eval<I: Isa>(self, isa: I) -> Self::Output {
let SimdTopK { logits, indices, k } = self;
let ops = isa.f32();
let mask_ops = isa.m32();
let compare_gt = |a: f32, b: f32| a.total_cmp(&b).reverse();
let mut topk: Vec<(u32, f32)> = indices
.iter()
.zip(logits)
.take(k)
.map(|(i, logit)| (*i, *logit))
.collect();
topk.sort_by(|a, b| compare_gt(a.1, b.1));
if k == 0 || logits.len() == k {
return topk;
}
let mut kth_logit = topk.last().unwrap().1;
let mut kth_logit_vec = ops.splat(kth_logit);
let mut update_topk = |kth_logit: &mut f32, index: u32, logit: f32| {
if logit > *kth_logit {
*topk.last_mut().unwrap() = (index, logit);
topk.sort_by(|a, b| compare_gt(a.1, b.1));
*kth_logit = topk.last().unwrap().1;
}
};
let indices = &indices[k..];
let logits = &logits[k..];
let mut indices_iter = indices.chunks_exact(ops.len());
let mut logits_iter = logits.simd_iter(ops);
for (index_chunk, logits_vec) in indices_iter.by_ref().zip(logits_iter.by_ref()) {
if mask_ops.any(ops.gt(logits_vec, kth_logit_vec)) {
for (&index, logit) in index_chunk.iter().zip(logits_vec.to_array()) {
update_topk(&mut kth_logit, index, logit);
}
kth_logit_vec = ops.splat(kth_logit);
}
}
if let Some((logits_tail, _mask)) = logits_iter.tail() {
let indices_tail = indices_iter.remainder();
for (&index, logit) in indices_tail.iter().zip(logits_tail.to_array()) {
update_topk(&mut kth_logit, index, logit);
}
}
topk
}
}
pub struct TopP {
cumulative_prob: f32,
normalize: bool,
}
impl TopP {
pub fn new(cumulative_prob: f32) -> Self {
Self {
cumulative_prob,
normalize: false,
}
}
pub fn normalize(mut self, normalize: bool) -> Self {
self.normalize = normalize;
self
}
}
impl LogitsFilter for TopP {
fn filter(&self, logits: Logits, _prev_tokens: &[TokenId]) -> Logits {
if self.cumulative_prob == 1.0 {
return logits;
}
let (mut logits, indices) = logits.into_logits_indices();
if self.normalize {
Softmax::new_mut(&mut logits).dispatch();
}
let mut pairs: Vec<(f32, TokenId)> = logits.into_iter().zip(indices).collect();
pairs.sort_by(|a, b| {
let (a_prob, _a_id) = a;
let (b_prob, _b_id) = b;
a_prob.total_cmp(b_prob).reverse()
});
let mut cum_prob = 0.;
let mut k = 0;
let threshold = self.cumulative_prob.max(f32::MIN_POSITIVE);
while cum_prob < threshold && k < pairs.len() {
cum_prob += pairs[k].0;
k += 1;
}
pairs.truncate(k);
let (logits, indices) = pairs.into_iter().unzip();
Logits::sparse(logits, indices)
}
}
#[derive(Default)]
pub struct Sort {
_private: (),
}
impl Sort {
pub fn new() -> Self {
Sort { _private: () }
}
}
impl LogitsFilter for Sort {
fn filter(&self, logits: Logits, _prev_tokens: &[TokenId]) -> Logits {
let (logits, indices) = logits.into_logits_indices();
let mut pairs: Vec<(f32, TokenId)> = logits.into_iter().zip(indices).collect();
pairs.sort_by(|(a_val, _), (b_val, _)| a_val.total_cmp(b_val).reverse());
let (logits, indices) = pairs.into_iter().unzip();
Logits::sparse(logits, indices)
}
}
#[cfg(test)]
mod tests {
use super::{Chain, Logits, LogitsFilter, Sort, Temperature, TopK, TopP, token_id_filter};
#[test]
fn test_token_id_filter() {
let logits = Logits::dense(vec![0., 1., 2., 3., 4.]);
let filter = token_id_filter(|id| id % 2 == 0);
let output = filter.filter(logits, &[]);
assert_eq!(output.logits(), &[0., 2., 4.]);
assert_eq!(output.indices(), &[0, 2, 4]);
}
#[test]
fn test_temperature() {
let logits = Logits::dense(vec![0., 1., 2., 3., 4.]);
let filter = Temperature::new(2.0);
let output = filter.filter(logits, &[]);
assert_eq!(output.logits(), &[0., 0.5, 1., 1.5, 2.0]);
assert_eq!(output.indices(), &[0, 1, 2, 3, 4]);
}
#[test]
fn test_chain() {
let logits = Logits::dense(vec![0., 1., 2., 3., 4.]);
let chain = Chain::new()
.append(token_id_filter(|id| id % 2 == 0))
.append(token_id_filter(|id| id > 0));
let output = chain.filter(logits, &[]);
assert_eq!(output.logits(), &[2., 4.]);
assert_eq!(output.indices(), &[2, 4]);
}
fn reference_topk(logits: &Logits, k: usize) -> Logits {
let mut pairs: Vec<(u32, f32)> = logits
.indices()
.iter()
.zip(logits.logits())
.map(|(idx, val)| (*idx, *val))
.collect();
pairs.sort_by(|a, b| a.1.total_cmp(&b.1).reverse());
pairs.truncate(k);
let (indices, logits) = pairs.into_iter().unzip();
Logits::sparse(logits, indices)
}
#[test]
fn test_top_k() {
let sort = |logits| Sort::new().filter(logits, &[]);
let logits = Logits::dense(vec![
-1., 1., 0., 2., -2., 10., -3., 2., 1., 0., 20., -5., 5., 0.1, -0.2, 0.2, 0.1,
]);
assert_eq!(logits.len(), 17);
for k in 0..=logits.len() {
let topk = TopK::new(k).filter(logits.clone(), &[]);
let sorted_topk = sort(topk);
let expected_topk = reference_topk(&logits, k);
assert_eq!(sorted_topk.logits(), expected_topk.logits());
assert_eq!(sorted_topk.indices(), expected_topk.indices());
}
let logits = Logits::dense(vec![]);
let topk = TopK::new(1).filter(logits, &[]);
assert!(topk.is_empty());
}
#[test]
fn test_top_p() {
let logits = Logits::dense(vec![0.1, 0.25, 0.15, 0.5]);
let all_logits = TopP::new(1.0).normalize(false).filter(logits.clone(), &[]);
assert_eq!(logits, all_logits);
let top_p_logits = TopP::new(0.5).normalize(false).filter(logits.clone(), &[]);
assert_eq!(top_p_logits.logits(), &[0.5]);
assert_eq!(top_p_logits.indices(), &[3]);
let top_p_logits = TopP::new(0.75).normalize(false).filter(logits.clone(), &[]);
assert_eq!(top_p_logits.logits(), &[0.5, 0.25]);
assert_eq!(top_p_logits.indices(), &[3, 1]);
let top_p_logits = TopP::new(0.).normalize(false).filter(logits.clone(), &[]);
assert_eq!(top_p_logits.logits(), &[0.5]);
assert_eq!(top_p_logits.indices(), &[3]);
}
}