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