Skip to main content

runtime/
sampler.rs

1//! Greedy sampler implementation
2
3use ndarray::{ArrayViewD, Axis};
4use std::f32;
5
6/// Greedy sampler that selects the token with the highest probability
7pub struct GreedySampler;
8
9impl GreedySampler {
10    /// Create a new greedy sampler
11    pub fn new() -> Self {
12        Self
13    }
14    
15    /// Sample the next token using greedy decoding
16    /// 
17    /// # Arguments
18    /// * `logits` - The logits from the model (unnormalized log probabilities)
19    /// 
20    /// # Returns
21    /// The token ID with the highest probability
22    pub fn sample(&self, logits: ArrayViewD<f32>) -> usize {
23        // For greedy sampling, we simply select the token with the highest logit
24        // In a real implementation, we would:
25        // 1. Apply temperature scaling if needed
26        // 2. Apply top-k or top-p filtering if needed
27        // 3. Select the token with the highest probability
28        
29        // Find the index of the maximum value
30        let mut max_idx = 0;
31        let mut max_val = f32::NEG_INFINITY;
32        
33        // Iterate through all elements to find the maximum
34        for (i, &val) in logits.iter().enumerate() {
35            if val > max_val {
36                max_val = val;
37                max_idx = i;
38            }
39        }
40        
41        max_idx
42    }
43    
44    /// Sample multiple tokens using greedy decoding
45    /// 
46    /// # Arguments
47    /// * `logits` - The logits from the model (batch_size x vocab_size)
48    /// 
49    /// # Returns
50    /// A vector of token IDs, one for each batch item
51    pub fn sample_batch(&self, logits: ArrayViewD<f32>) -> Vec<usize> {
52        // For batch sampling, we sample each batch item independently
53        let batch_size = logits.shape()[0];
54        
55        let mut samples = Vec::with_capacity(batch_size);
56        
57        for batch_idx in 0..batch_size {
58            // Extract logits for this batch item
59            let batch_logits = logits.index_axis(Axis(0), batch_idx);
60            
61            // Sample using the regular sample method
62            let token_id = self.sample(batch_logits.view());
63            samples.push(token_id);
64        }
65        
66        samples
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use ndarray::ArrayD;
74    
75    #[test]
76    fn test_greedy_sampler_single() {
77        let sampler = GreedySampler::new();
78        
79        // Create test logits where the highest value is at index 3
80        let logits = ArrayD::from_shape_vec(vec![5], vec![0.1, 0.2, 0.3, 0.9, 0.4]).unwrap();
81        let logits_view = logits.view();
82        
83        let token_id = sampler.sample(logits_view);
84        assert_eq!(token_id, 3);
85    }
86    
87    #[test]
88    fn test_greedy_sampler_batch() {
89        let sampler = GreedySampler::new();
90        
91        // Create test logits for a batch of 2
92        let logits = ArrayD::from_shape_vec(vec![2, 3], vec![
93            0.1, 0.8, 0.3,  // First batch item - max at index 1
94            0.6, 0.2, 0.9,  // Second batch item - max at index 2
95        ]).unwrap();
96        let logits_view = logits.view();
97        
98        let token_ids = sampler.sample_batch(logits_view);
99        assert_eq!(token_ids, vec![1, 2]);
100    }
101}