1use rlx_ir::Philox4x32;
50
51pub type Logits<'a> = &'a mut [f32];
53
54#[derive(Debug, Default, Clone)]
56pub struct SamplerState {
57 pub mirostat_mu: f32,
59}
60
61impl SamplerState {
62 pub fn new() -> Self {
63 Self {
64 mirostat_mu: f32::NAN,
65 }
66 }
67}
68
69pub trait Sampler: std::fmt::Debug + Send + Sync {
72 fn apply(
76 &self,
77 logits: Logits<'_>,
78 history: &[u32],
79 state: &mut SamplerState,
80 rng: &mut Philox4x32,
81 );
82
83 fn name(&self) -> &'static str {
85 std::any::type_name::<Self>()
86 }
87}
88
89#[derive(Debug)]
92pub struct SamplerChain {
93 pub steps: Vec<Box<dyn Sampler>>,
94}
95
96impl SamplerChain {
97 pub fn new() -> Self {
98 Self { steps: Vec::new() }
99 }
100
101 pub fn builder() -> SamplerChainBuilder {
102 SamplerChainBuilder::default()
103 }
104
105 pub fn sample(
107 &self,
108 logits: Logits<'_>,
109 history: &[u32],
110 state: &mut SamplerState,
111 rng: &mut Philox4x32,
112 ) -> u32 {
113 for step in &self.steps {
114 step.apply(logits, history, state, rng);
115 }
116 sample_from_logits(logits, rng)
117 }
118}
119
120impl Default for SamplerChain {
121 fn default() -> Self {
122 Self::new()
123 }
124}
125
126#[derive(Debug, Default)]
127pub struct SamplerChainBuilder {
128 steps: Vec<Box<dyn Sampler>>,
129}
130
131impl SamplerChainBuilder {
132 pub fn push<S: Sampler + 'static>(mut self, s: S) -> Self {
133 self.steps.push(Box::new(s));
134 self
135 }
136
137 pub fn push_boxed(mut self, s: Box<dyn Sampler>) -> Self {
138 self.steps.push(s);
139 self
140 }
141
142 pub fn build(self) -> SamplerChain {
143 SamplerChain { steps: self.steps }
144 }
145}
146
147pub fn softmax_inplace(logits: &mut [f32]) {
151 let mut maxv = f32::NEG_INFINITY;
152 for &x in logits.iter() {
153 if x > maxv {
154 maxv = x;
155 }
156 }
157 if !maxv.is_finite() {
158 let inv = 1.0 / logits.len() as f32;
159 for x in logits.iter_mut() {
160 *x = inv;
161 }
162 return;
163 }
164 let mut s = 0.0f32;
165 for x in logits.iter_mut() {
166 let v = (*x - maxv).exp();
167 *x = v;
168 s += v;
169 }
170 let inv = if s > 0.0 { 1.0 / s } else { 0.0 };
171 for x in logits.iter_mut() {
172 *x *= inv;
173 }
174}
175
176pub fn sample_from_probs(probs: &[f32], rng: &mut Philox4x32) -> u32 {
179 let r = rng.next_f32();
180 let mut acc = 0.0f32;
181 for (i, &p) in probs.iter().enumerate() {
182 acc += p;
183 if r <= acc {
184 return i as u32;
185 }
186 }
187 (probs.len() - 1) as u32
188}
189
190pub fn sample_from_logits(logits: &mut [f32], rng: &mut Philox4x32) -> u32 {
192 softmax_inplace(logits);
193 sample_from_probs(logits, rng)
194}
195
196fn sorted_desc(logits: &[f32]) -> Vec<(usize, f32)> {
198 let mut v: Vec<(usize, f32)> = logits.iter().copied().enumerate().collect();
199 v.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
200 v
201}
202
203#[derive(Debug, Clone, Copy)]
206pub struct Temperature {
207 pub t: f32,
208}
209
210impl Sampler for Temperature {
211 fn apply(&self, logits: Logits<'_>, _h: &[u32], _s: &mut SamplerState, _r: &mut Philox4x32) {
212 let t = self.t.max(1e-6);
213 for x in logits.iter_mut() {
214 *x /= t;
215 }
216 }
217}
218
219#[derive(Debug, Clone, Copy)]
226pub struct DynamicTemperature {
227 pub min: f32,
228 pub max: f32,
229 pub exponent: f32,
230}
231
232impl Sampler for DynamicTemperature {
233 fn apply(&self, logits: Logits<'_>, _h: &[u32], _s: &mut SamplerState, _r: &mut Philox4x32) {
234 let v = logits.len();
235 if v == 0 {
236 return;
237 }
238 let mut tmp: Vec<f32> = logits.to_vec();
239 softmax_inplace(&mut tmp);
240 let mut h = 0.0f32;
242 for &p in tmp.iter() {
243 if p > 0.0 {
244 h -= p * p.ln();
245 }
246 }
247 let hmax = (v as f32).ln().max(1e-6);
248 let norm = (h / hmax).clamp(0.0, 1.0);
249 let t = self.min + (self.max - self.min) * norm.powf(self.exponent);
250 let t = t.max(1e-6);
251 for x in logits.iter_mut() {
252 *x /= t;
253 }
254 }
255}
256
257#[derive(Debug, Clone, Copy)]
260pub struct TopK {
261 pub k: usize,
262}
263
264impl Sampler for TopK {
265 fn apply(&self, logits: Logits<'_>, _h: &[u32], _s: &mut SamplerState, _r: &mut Philox4x32) {
266 let v = logits.len();
267 if self.k == 0 || self.k >= v {
268 return;
269 }
270 let sorted = sorted_desc(logits);
271 let cutoff = sorted[self.k - 1].1;
272 for x in logits.iter_mut() {
273 if *x < cutoff {
274 *x = f32::NEG_INFINITY;
275 }
276 }
277 }
278}
279
280#[derive(Debug, Clone, Copy)]
283pub struct TopP {
284 pub p: f32,
285 pub min_keep: usize,
288}
289
290impl Sampler for TopP {
291 fn apply(&self, logits: Logits<'_>, _h: &[u32], _s: &mut SamplerState, _r: &mut Philox4x32) {
292 if self.p >= 1.0 {
293 return;
294 }
295 let v = logits.len();
296 if v == 0 {
297 return;
298 }
299 let mut probs: Vec<f32> = logits.to_vec();
300 softmax_inplace(&mut probs);
301 let sorted = sorted_desc(&probs);
302 let mut keep = vec![false; v];
303 let mut cum = 0.0f32;
304 for (rank, (idx, p)) in sorted.iter().enumerate() {
305 keep[*idx] = true;
306 cum += *p;
307 if cum >= self.p && rank + 1 >= self.min_keep {
308 break;
309 }
310 }
311 for (i, x) in logits.iter_mut().enumerate() {
312 if !keep[i] {
313 *x = f32::NEG_INFINITY;
314 }
315 }
316 }
317}
318
319#[derive(Debug, Clone, Copy)]
328pub struct MinP {
329 pub p: f32,
330 pub min_keep: usize,
333}
334
335impl Sampler for MinP {
336 fn apply(&self, logits: Logits<'_>, _h: &[u32], _s: &mut SamplerState, _r: &mut Philox4x32) {
337 if self.p <= 0.0 || self.p > 1.0 || self.p.is_nan() {
340 return;
341 }
342 let v = logits.len();
343 if v == 0 {
344 return;
345 }
346 let mut probs: Vec<f32> = logits.to_vec();
347 softmax_inplace(&mut probs);
348 let p_max = probs.iter().copied().fold(f32::NEG_INFINITY, f32::max);
349 if p_max <= 0.0 || p_max.is_nan() {
350 return;
351 }
352 let threshold = self.p * p_max;
353 let mut keep: Vec<bool> = probs.iter().map(|&p| p >= threshold).collect();
354 let floor = self.min_keep.max(1);
356 if keep.iter().filter(|&&k| k).count() < floor {
357 for (idx, _) in sorted_desc(&probs).into_iter().take(floor) {
358 keep[idx] = true;
359 }
360 }
361 for (i, x) in logits.iter_mut().enumerate() {
362 if !keep[i] {
363 *x = f32::NEG_INFINITY;
364 }
365 }
366 }
367}
368
369pub fn apply_logit_bias(logits: &mut [f32], bias: &[(u32, f32)]) {
375 let v = logits.len();
376 for &(id, b) in bias {
377 let i = id as usize;
378 if i < v {
379 logits[i] += b;
380 }
381 }
382}
383
384#[derive(Debug, Clone, Copy)]
391pub struct TopNSigma {
392 pub n: f32,
393}
394
395impl Sampler for TopNSigma {
396 fn apply(&self, logits: Logits<'_>, _h: &[u32], _s: &mut SamplerState, _r: &mut Philox4x32) {
397 let v = logits.len();
398 if v == 0 || !self.n.is_finite() || self.n <= 0.0 {
399 return;
400 }
401 let mut maxv = f32::NEG_INFINITY;
402 let mut count = 0usize;
403 let mut sum = 0.0f32;
404 for &x in logits.iter() {
405 if x.is_finite() {
406 if x > maxv {
407 maxv = x;
408 }
409 sum += x;
410 count += 1;
411 }
412 }
413 if count == 0 || !maxv.is_finite() {
414 return;
415 }
416 let mean = sum / count as f32;
417 let mut var = 0.0f32;
418 for &x in logits.iter() {
419 if x.is_finite() {
420 let d = x - mean;
421 var += d * d;
422 }
423 }
424 let sigma = (var / count as f32).sqrt();
425 let cutoff = maxv - self.n * sigma;
426 for x in logits.iter_mut() {
427 if *x < cutoff {
428 *x = f32::NEG_INFINITY;
429 }
430 }
431 }
432}
433
434#[derive(Debug, Clone, Copy)]
441pub struct TypicalP {
442 pub p: f32,
443 pub min_keep: usize,
444}
445
446impl Sampler for TypicalP {
447 fn apply(&self, logits: Logits<'_>, _h: &[u32], _s: &mut SamplerState, _r: &mut Philox4x32) {
448 if self.p >= 1.0 {
449 return;
450 }
451 let v = logits.len();
452 if v == 0 {
453 return;
454 }
455 let mut probs: Vec<f32> = logits.to_vec();
456 softmax_inplace(&mut probs);
457 let mut h = 0.0f32;
458 for &p in probs.iter() {
459 if p > 0.0 {
460 h -= p * p.ln();
461 }
462 }
463 let mut scored: Vec<(usize, f32, f32)> = probs
465 .iter()
466 .enumerate()
467 .map(|(i, &p)| {
468 let neg_log = if p > 0.0 { -p.ln() } else { f32::INFINITY };
469 let dev = (neg_log - h).abs();
470 (i, p, dev)
471 })
472 .collect();
473 scored.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));
474 let mut keep = vec![false; v];
475 let mut cum = 0.0f32;
476 for (rank, (idx, p, _)) in scored.iter().enumerate() {
477 keep[*idx] = true;
478 cum += *p;
479 if cum >= self.p && rank + 1 >= self.min_keep {
480 break;
481 }
482 }
483 for (i, x) in logits.iter_mut().enumerate() {
484 if !keep[i] {
485 *x = f32::NEG_INFINITY;
486 }
487 }
488 }
489}
490
491#[derive(Debug, Clone, Copy)]
498pub struct MirostatV1 {
499 pub tau: f32,
500 pub eta: f32,
501 pub m: usize,
503}
504
505impl Default for MirostatV1 {
506 fn default() -> Self {
507 Self {
508 tau: 5.0,
509 eta: 0.1,
510 m: 100,
511 }
512 }
513}
514
515impl Sampler for MirostatV1 {
516 fn apply(
517 &self,
518 logits: Logits<'_>,
519 _h: &[u32],
520 state: &mut SamplerState,
521 rng: &mut Philox4x32,
522 ) {
523 let v = logits.len();
524 if v == 0 {
525 return;
526 }
527 if !state.mirostat_mu.is_finite() {
528 state.mirostat_mu = 2.0 * self.tau;
529 }
530 let mu = state.mirostat_mu.max(1e-6);
531 let mut probs = logits.to_vec();
533 softmax_inplace(&mut probs);
534 let sorted = sorted_desc(&probs);
535 let m = self.m.min(sorted.len()).max(2);
537 let mut num = 0.0f32;
538 let mut den = 0.0f32;
539 for i in 0..(m - 1) {
540 let t = ((i + 2) as f32 / (i + 1) as f32).ln();
541 let b = (sorted[i].1 / sorted[i + 1].1).ln().max(1e-9);
542 num += t * b;
543 den += t * t;
544 }
545 let s_hat = if den > 0.0 { num / den } else { 1.0 };
546 let eps = (s_hat - 1.0).abs().max(1e-3);
548 let k_real = ((eps * (2.0f32.powf(mu))) / (1.0 - (v as f32).powf(-eps)))
549 .powf(1.0 / s_hat)
550 .clamp(1.0, v as f32);
551 let k = k_real as usize;
552 if k < sorted.len() {
553 let cutoff = sorted[k - 1].1;
554 for (i, p) in probs.iter_mut().enumerate() {
555 if *p < cutoff {
556 *p = 0.0;
557 }
558 let _ = i;
559 }
560 let s: f32 = probs.iter().sum();
561 if s > 0.0 {
562 for p in probs.iter_mut() {
563 *p /= s;
564 }
565 }
566 }
567 let tok = sample_from_probs(&probs, rng) as usize;
570 let surprise = if probs[tok] > 0.0 {
571 -probs[tok].ln() / 2.0f32.ln()
572 } else {
573 mu
574 };
575 state.mirostat_mu = (mu - self.eta * (surprise - self.tau)).max(0.0);
576 for (i, x) in logits.iter_mut().enumerate() {
577 *x = if i == tok {
578 f32::INFINITY
579 } else {
580 f32::NEG_INFINITY
581 };
582 }
583 }
584}
585
586#[derive(Debug, Clone, Copy)]
592pub struct MirostatV2 {
593 pub tau: f32,
594 pub eta: f32,
595}
596
597impl Default for MirostatV2 {
598 fn default() -> Self {
599 Self { tau: 5.0, eta: 0.1 }
600 }
601}
602
603impl Sampler for MirostatV2 {
604 fn apply(
605 &self,
606 logits: Logits<'_>,
607 _h: &[u32],
608 state: &mut SamplerState,
609 rng: &mut Philox4x32,
610 ) {
611 let v = logits.len();
612 if v == 0 {
613 return;
614 }
615 if !state.mirostat_mu.is_finite() {
616 state.mirostat_mu = 2.0 * self.tau;
617 }
618 let mu = state.mirostat_mu;
619 let mut probs = logits.to_vec();
620 softmax_inplace(&mut probs);
621 let mut sorted = sorted_desc(&probs);
623 let ln2 = 2.0f32.ln();
624 let mut keep_n = 0usize;
625 for (i, (_, p)) in sorted.iter().enumerate() {
626 let s = if *p > 0.0 {
627 -p.ln() / ln2
628 } else {
629 f32::INFINITY
630 };
631 if s > mu {
632 break;
633 }
634 keep_n = i + 1;
635 }
636 if keep_n == 0 {
637 keep_n = 1;
638 }
639 let kept: std::collections::HashSet<usize> =
640 sorted.drain(..keep_n).map(|(i, _)| i).collect();
641 for (i, p) in probs.iter_mut().enumerate() {
642 if !kept.contains(&i) {
643 *p = 0.0;
644 }
645 }
646 let s: f32 = probs.iter().sum();
647 if s > 0.0 {
648 for p in probs.iter_mut() {
649 *p /= s;
650 }
651 }
652 let tok = sample_from_probs(&probs, rng) as usize;
653 let surprise = if probs[tok] > 0.0 {
654 -probs[tok].ln() / ln2
655 } else {
656 mu
657 };
658 state.mirostat_mu = (mu - self.eta * (surprise - self.tau)).max(0.0);
659 for (i, x) in logits.iter_mut().enumerate() {
660 *x = if i == tok {
661 f32::INFINITY
662 } else {
663 f32::NEG_INFINITY
664 };
665 }
666 }
667}
668
669#[derive(Debug, Clone, Copy)]
676pub struct Xtc {
677 pub threshold: f32,
678 pub prob: f32,
679 pub min_keep: usize,
681}
682
683impl Sampler for Xtc {
684 fn apply(&self, logits: Logits<'_>, _h: &[u32], _s: &mut SamplerState, rng: &mut Philox4x32) {
685 if self.prob <= 0.0 {
686 return;
687 }
688 if rng.next_f32() > self.prob {
689 return;
690 }
691 let v = logits.len();
692 if v == 0 {
693 return;
694 }
695 let mut probs = logits.to_vec();
696 softmax_inplace(&mut probs);
697 let sorted = sorted_desc(&probs);
698 let n_above = sorted.iter().filter(|(_, p)| *p > self.threshold).count();
700 if n_above < 2 {
701 return; }
703 let to_kill = n_above.saturating_sub(self.min_keep.max(1));
706 for (idx, _) in sorted.iter().take(to_kill) {
707 logits[*idx] = f32::NEG_INFINITY;
708 }
709 }
710}
711
712#[derive(Debug, Clone)]
720pub struct Dry {
721 pub multiplier: f32,
722 pub base: f32,
723 pub allowed_length: usize,
724 pub max_ngram: usize,
725 pub sequence_breakers: Vec<u32>,
727}
728
729impl Default for Dry {
730 fn default() -> Self {
731 Self {
732 multiplier: 0.8,
733 base: 1.75,
734 allowed_length: 2,
735 max_ngram: 32,
736 sequence_breakers: Vec::new(),
737 }
738 }
739}
740
741impl Sampler for Dry {
742 fn apply(
743 &self,
744 logits: Logits<'_>,
745 history: &[u32],
746 _s: &mut SamplerState,
747 _r: &mut Philox4x32,
748 ) {
749 if self.multiplier <= 0.0 || history.is_empty() {
750 return;
751 }
752 let n = history.len();
753 let max_ngram = self.max_ngram.min(n);
754 let breakers: std::collections::HashSet<u32> =
755 self.sequence_breakers.iter().copied().collect();
756 let mut longest: std::collections::HashMap<u32, usize> = std::collections::HashMap::new();
761 for i in 0..n.saturating_sub(1) {
762 if breakers.contains(&history[i]) {
763 continue;
764 }
765 let mut l = 0usize;
768 while l < max_ngram && i >= l && n > l && history[i - l] == history[n - 1 - l] {
769 l += 1;
770 }
771 if l >= self.allowed_length && i + 1 < n {
772 let next = history[i + 1];
773 let cur = longest.entry(next).or_insert(0);
774 if l > *cur {
775 *cur = l;
776 }
777 }
778 }
779 for (tok, l) in longest {
780 let pen = self.multiplier * self.base.powi((l - self.allowed_length) as i32);
781 let idx = tok as usize;
782 if idx < logits.len() {
783 logits[idx] -= pen;
784 }
785 }
786 }
787}
788
789#[derive(Debug, Clone, Copy)]
792pub struct RepetitionPenalty {
793 pub penalty: f32,
794 pub frequency: f32,
795 pub presence: f32,
796 pub last_n: usize,
798}
799
800impl Default for RepetitionPenalty {
801 fn default() -> Self {
802 Self {
803 penalty: 1.0,
804 frequency: 0.0,
805 presence: 0.0,
806 last_n: 64,
807 }
808 }
809}
810
811impl Sampler for RepetitionPenalty {
812 fn apply(
813 &self,
814 logits: Logits<'_>,
815 history: &[u32],
816 _s: &mut SamplerState,
817 _r: &mut Philox4x32,
818 ) {
819 if history.is_empty() {
820 return;
821 }
822 let start = history.len().saturating_sub(self.last_n);
823 let window = &history[start..];
824 let mut counts: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
825 for &t in window {
826 *counts.entry(t).or_insert(0) += 1;
827 }
828 for (tok, c) in counts {
829 let idx = tok as usize;
830 if idx >= logits.len() {
831 continue;
832 }
833 logits[idx] -= self.presence + self.frequency * c as f32;
835 if (self.penalty - 1.0).abs() > 1e-6 {
838 if logits[idx] > 0.0 {
839 logits[idx] /= self.penalty;
840 } else {
841 logits[idx] *= self.penalty;
842 }
843 }
844 }
845 }
846}
847
848#[cfg(test)]
851mod tests {
852 use super::*;
853
854 fn rng() -> Philox4x32 {
855 Philox4x32::new(0xDEAD_BEEF)
856 }
857
858 #[test]
859 fn temperature_zero_is_greedy_after_chain() {
860 let chain = SamplerChain::builder()
861 .push(Temperature { t: 1e-6 })
862 .build();
863 let mut state = SamplerState::new();
864 let mut r = rng();
865 let mut logits = vec![1.0, 5.0, 2.0, 3.0];
866 let tok = chain.sample(&mut logits, &[], &mut state, &mut r);
867 assert_eq!(tok, 1);
868 }
869
870 #[test]
871 fn top_k_masks_below_kth() {
872 let mut logits = vec![1.0, 5.0, 2.0, 3.0];
873 let mut s = SamplerState::new();
874 let mut r = rng();
875 TopK { k: 2 }.apply(&mut logits, &[], &mut s, &mut r);
876 assert_eq!(logits[1], 5.0);
877 assert_eq!(logits[3], 3.0);
878 assert!(logits[0].is_infinite() && logits[0] < 0.0);
879 assert!(logits[2].is_infinite() && logits[2] < 0.0);
880 }
881
882 #[test]
883 fn top_p_keeps_nucleus() {
884 let mut logits = vec![0.0f32; 4];
885 logits[0] = 10.0;
886 logits[1] = 5.0;
887 let mut s = SamplerState::new();
888 let mut r = rng();
889 TopP {
890 p: 0.5,
891 min_keep: 1,
892 }
893 .apply(&mut logits, &[], &mut s, &mut r);
894 assert!(logits[0].is_finite());
895 assert!(logits[2].is_infinite() && logits[2] < 0.0);
897 assert!(logits[3].is_infinite() && logits[3] < 0.0);
898 }
899
900 #[test]
901 fn min_p_scales_cutoff_by_top_prob() {
902 let mut logits = vec![0.0f32; 4];
904 logits[0] = 10.0;
905 logits[1] = 2.0;
906 let mut s = SamplerState::new();
907 let mut r = rng();
908 MinP {
909 p: 0.1,
910 min_keep: 1,
911 }
912 .apply(&mut logits, &[], &mut s, &mut r);
913 assert!(logits[0].is_finite());
914 assert!(logits[1].is_infinite() && logits[1] < 0.0);
915 assert!(logits[2].is_infinite() && logits[2] < 0.0);
916 }
917
918 #[test]
919 fn min_p_min_keep_admits_top_tokens() {
920 let mut logits = vec![10.0, 9.0, 1.0, 0.0];
922 let mut s = SamplerState::new();
923 let mut r = rng();
924 MinP {
925 p: 0.99,
926 min_keep: 2,
927 }
928 .apply(&mut logits, &[], &mut s, &mut r);
929 assert!(logits[0].is_finite() && logits[1].is_finite());
930 }
931
932 #[test]
933 fn logit_bias_is_additive_and_bounds_checked() {
934 let mut logits = vec![0.0f32, 1.0, 2.0];
935 apply_logit_bias(&mut logits, &[(1, 5.0), (99, 1.0)]); assert_eq!(logits, vec![0.0, 6.0, 2.0]);
937 }
938
939 #[test]
940 fn top_n_sigma_keeps_top_logits() {
941 let mut logits = vec![0.0f32; 32];
943 logits[0] = 10.0;
944 logits[1] = 9.5;
945 let mut s = SamplerState::new();
946 let mut r = rng();
947 TopNSigma { n: 1.0 }.apply(&mut logits, &[], &mut s, &mut r);
948 assert!(logits[0].is_finite());
950 assert!(logits[5].is_infinite() && logits[5] < 0.0);
951 }
952
953 #[test]
954 fn dynamic_temperature_scales_with_entropy() {
955 let mut logits = vec![1.0f32; 16];
957 let before = logits.clone();
958 let mut s = SamplerState::new();
959 let mut r = rng();
960 DynamicTemperature {
961 min: 0.5,
962 max: 2.0,
963 exponent: 1.0,
964 }
965 .apply(&mut logits, &[], &mut s, &mut r);
966 assert!((logits[0] - before[0] / 2.0).abs() < 1e-5);
967 }
968
969 #[test]
970 fn typical_p_keeps_typical_token() {
971 let mut logits = vec![5.0, 4.0, 0.0, -10.0];
972 let mut s = SamplerState::new();
973 let mut r = rng();
974 TypicalP {
975 p: 0.5,
976 min_keep: 1,
977 }
978 .apply(&mut logits, &[], &mut s, &mut r);
979 assert!(logits.iter().any(|x| x.is_finite()));
981 }
982
983 #[test]
984 fn mirostat_v2_keeps_at_least_one() {
985 let mut logits = vec![1.0, 2.0, 3.0, 4.0];
986 let mut s = SamplerState::new();
987 let mut r = rng();
988 MirostatV2 { tau: 5.0, eta: 0.1 }.apply(&mut logits, &[], &mut s, &mut r);
989 let n_inf = logits
991 .iter()
992 .filter(|x| x.is_infinite() && **x > 0.0)
993 .count();
994 assert_eq!(n_inf, 1);
995 }
996
997 #[test]
998 fn xtc_disabled_when_prob_zero() {
999 let mut logits = vec![10.0, 5.0, 1.0];
1000 let before = logits.clone();
1001 let mut s = SamplerState::new();
1002 let mut r = rng();
1003 Xtc {
1004 threshold: 0.5,
1005 prob: 0.0,
1006 min_keep: 1,
1007 }
1008 .apply(&mut logits, &[], &mut s, &mut r);
1009 assert_eq!(logits, before);
1010 }
1011
1012 #[test]
1013 fn dry_penalises_repeat_continuation() {
1014 let history = vec![0u32, 1, 0, 1, 0];
1016 let mut logits = vec![0.0, 0.0];
1017 let mut s = SamplerState::new();
1018 let mut r = rng();
1019 Dry {
1020 multiplier: 1.0,
1021 base: 2.0,
1022 allowed_length: 2,
1023 max_ngram: 8,
1024 sequence_breakers: vec![],
1025 }
1026 .apply(&mut logits, &history, &mut s, &mut r);
1027 assert!(logits[1] < 0.0, "B should be penalised; got {}", logits[1]);
1028 }
1029
1030 #[test]
1031 fn repetition_penalty_lowers_repeated_token() {
1032 let history = vec![0u32; 8];
1033 let mut logits = vec![1.0, 1.0];
1034 let mut s = SamplerState::new();
1035 let mut r = rng();
1036 RepetitionPenalty {
1037 penalty: 2.0,
1038 frequency: 0.0,
1039 presence: 0.0,
1040 last_n: 64,
1041 }
1042 .apply(&mut logits, &history, &mut s, &mut r);
1043 assert!(logits[0] < logits[1]);
1044 }
1045}