metrale_sampling/
sample_impl.rs1use super::{SamplingParams, apply_penalties_and_bias, record_entropy};
13
14pub fn sample_with_params_history(
15 data: &[u8],
16 params: &SamplingParams,
17 token_history: &[u32],
18) -> u32 {
19 sample_with_params_seeded(data, params, token_history, params.seed)
20}
21
22pub fn sample_with_params_seeded(
27 data: &[u8],
28 params: &SamplingParams,
29 token_history: &[u32],
30 seed: Option<u64>,
31) -> u32 {
32 let n = data.len() / 4;
33 let top_k = params.top_k as usize;
34 let top_p = params.top_p;
35 let top_n_sigma = params.top_n_sigma;
36 let min_p = params.min_p;
37
38 let mut raw_logits: Vec<f32> = data
39 .chunks_exact(4)
40 .take(n)
41 .map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
42 .collect();
43
44 apply_penalties_and_bias(&mut raw_logits, params, token_history);
47
48 if params.temperature <= 0.0 {
52 return greedy_pick_last_wins(&raw_logits);
53 }
54 let temperature = params.temperature;
55
56 if top_n_sigma > 0.0 {
59 let sum: f32 = raw_logits.iter().sum();
60 let mean = sum / n as f32;
61 let var: f32 = raw_logits.iter().map(|x| (x - mean).powi(2)).sum::<f32>() / n as f32;
62 let sigma = var.sqrt();
63 if sigma > 0.0 {
64 let threshold = mean - top_n_sigma * sigma;
65 for logit in raw_logits.iter_mut() {
66 if *logit < threshold {
67 *logit = f32::NEG_INFINITY;
68 }
69 }
70 }
71 }
72
73 let mut logits: Vec<(u32, f32)> = raw_logits
74 .iter()
75 .enumerate()
76 .filter(|(_, v)| v.is_finite())
77 .map(|(i, v)| (i as u32, v / temperature))
78 .collect();
79
80 if logits.is_empty() {
81 return raw_logits
82 .iter()
83 .enumerate()
84 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
85 .map(|(i, _)| i as u32)
86 .unwrap_or(0);
87 }
88
89 let cmp_desc =
94 |a: &(u32, f32), b: &(u32, f32)| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal);
95 let sorted = if top_k > 0 && top_k < logits.len() {
96 logits.select_nth_unstable_by(top_k, cmp_desc);
97 logits.truncate(top_k);
98 logits.sort_unstable_by(cmp_desc);
99 true
100 } else if min_p > 0.0 || top_p < 1.0 {
101 logits.sort_unstable_by(cmp_desc);
102 true
103 } else {
104 false
105 };
106
107 let max_val = if sorted {
111 logits[0].1
112 } else {
113 logits
114 .iter()
115 .map(|&(_, v)| v)
116 .fold(f32::NEG_INFINITY, f32::max)
117 };
118 let mut probs: Vec<(u32, f32)> = logits
119 .iter()
120 .map(|&(idx, logit)| (idx, (logit - max_val).exp()))
121 .collect();
122
123 {
126 let sum: f32 = probs.iter().map(|p| p.1).sum();
127 if sum > 0.0 {
128 let inv = 1.0 / sum;
129 let h: f32 = probs
130 .iter()
131 .map(|&(_, w)| {
132 let p = w * inv;
133 if p > 1e-10 { -p * p.ln() } else { 0.0 }
134 })
135 .sum();
136 record_entropy(h);
137 }
138 }
139
140 if min_p > 0.0 {
141 let max_prob = probs[0].1;
142 let threshold = min_p * max_prob;
143 probs.retain(|p| p.1 >= threshold);
144 }
145
146 if top_p < 1.0 {
147 let sum: f32 = probs.iter().map(|p| p.1).sum();
148 let mut cumsum = 0.0f32;
149 let mut cutoff = probs.len();
150 for (i, &(_, prob)) in probs.iter().enumerate() {
151 cumsum += prob / sum;
152 if cumsum >= top_p {
153 cutoff = i + 1;
154 break;
155 }
156 }
157 probs.truncate(cutoff);
158 }
159
160 let sum: f32 = probs.iter().map(|p| p.1).sum();
161 let random_val: f32 = if let Some(s) = seed {
162 use rand::Rng;
163 use rand::SeedableRng;
164 let mut rng = rand::rngs::StdRng::seed_from_u64(s);
165 rng.random::<f32>()
166 } else {
167 rand::random::<f32>()
168 };
169 let threshold = random_val * sum;
170 let mut cumsum = 0.0f32;
171 for &(idx, prob) in &probs {
172 cumsum += prob;
173 if cumsum >= threshold {
174 return idx;
175 }
176 }
177 probs.last().map_or(0, |p| p.0)
178}
179
180fn greedy_pick_last_wins(v: &[f32]) -> u32 {
192 const LANES: usize = 8;
193 let mut acc = [f32::NEG_INFINITY; LANES];
194 let mut any_nan = false;
195 let mut chunks = v.chunks_exact(LANES);
196 for c in &mut chunks {
197 for (a, &x) in acc.iter_mut().zip(c) {
198 any_nan |= x.is_nan();
199 if x > *a {
200 *a = x;
201 }
202 }
203 }
204 let mut best = f32::NEG_INFINITY;
205 for &a in acc.iter() {
206 if a > best {
207 best = a;
208 }
209 }
210 for &x in chunks.remainder() {
211 any_nan |= x.is_nan();
212 if x > best {
213 best = x;
214 }
215 }
216 if any_nan {
217 return v
218 .iter()
219 .enumerate()
220 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
221 .map(|(i, _)| i as u32)
222 .unwrap_or(0);
223 }
224 v.iter()
225 .rposition(|&x| x == best)
226 .unwrap_or(0)
227 .try_into()
228 .unwrap_or(0)
229}
230
231#[cfg(test)]
232mod greedy_tests {
233 use super::greedy_pick_last_wins;
234
235 fn reference(v: &[f32]) -> u32 {
236 v.iter()
237 .enumerate()
238 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
239 .map(|(i, _)| i as u32)
240 .unwrap_or(0)
241 }
242
243 fn agree(v: &[f32]) {
244 assert_eq!(greedy_pick_last_wins(v), reference(v), "diverged on {v:?}");
245 }
246
247 #[test]
248 fn matches_max_by_reference() {
249 agree(&[]);
250 agree(&[1.0]);
251 agree(&[1.0, 3.0, 2.0]);
252 agree(&[1.0, 5.0, 5.0, 5.0, 2.0]);
253 agree(&[5.0, 1.0, 5.0]);
254 agree(&[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 9.0, 9.0]);
257 agree(&[-5.0, -1.0, -3.0]);
258 agree(&[-0.0, 0.0, -0.0]);
259 agree(&[f32::NEG_INFINITY, f32::NEG_INFINITY]);
260 agree(&[f32::INFINITY, 1.0, f32::INFINITY]);
261 agree(&[f32::NAN, 1.0, 2.0]);
262 agree(&[1.0, 2.0, f32::NAN]);
263 agree(&[f32::NAN]);
264 }
265
266 #[test]
267 fn vocab_sized_last_wins() {
268 let mut v: Vec<f32> = (0..248_320)
269 .map(|i| (((i * 2654435761u64 as usize) % 100_003) as f32) / 1000.0 - 50.0)
270 .collect();
271 v[100_000] = 999.0;
272 v[200_000] = 999.0;
273 assert_eq!(greedy_pick_last_wins(&v), reference(&v));
274 assert_eq!(greedy_pick_last_wins(&v), 200_000);
275 }
276}