metrale_sampling/
penalties.rs1use super::SamplingParams;
13
14pub fn apply_lz_penalty(logits: &mut [f32], history: &[u32], penalty: f32) {
21 use std::collections::HashSet;
22 const LZ_WINDOW: usize = 256;
23 let history = if history.len() > LZ_WINDOW {
24 &history[history.len() - LZ_WINDOW..]
25 } else {
26 history
27 };
28 let n = logits.len();
29 let token_set: HashSet<u32> = history.iter().copied().collect();
31 for &candidate in &token_set {
32 if (candidate as usize) >= n {
33 continue;
34 }
35 for ngram_len in 3..=5usize {
36 if history.len() < ngram_len {
37 continue;
38 }
39 let suffix = &history[history.len() - (ngram_len - 1)..];
42 let count = history
43 .windows(ngram_len)
44 .filter(|w| w[..ngram_len - 1] == *suffix && w[ngram_len - 1] == candidate)
45 .count();
46 if count > 0 {
47 logits[candidate as usize] -= penalty * (ngram_len as f32 - 2.0) * count as f32;
48 }
49 }
50 }
51}
52
53pub fn apply_dry_penalty(
62 logits: &mut [f32],
63 history: &[u32],
64 multiplier: f32,
65 base: f32,
66 allowed_length: u32,
67 breakers: &[u32],
68) {
69 if history.is_empty() || multiplier == 0.0 {
70 return;
71 }
72 let n = logits.len();
73 let hist_len = history.len();
74 let allowed = allowed_length as usize;
75
76 let mut match_lengths = vec![0usize; hist_len];
77 for i in (0..hist_len.saturating_sub(1)).rev() {
78 if breakers.contains(&history[i]) {
79 match_lengths[i] = 0;
80 continue;
81 }
82 let mut len = 0;
83 let mut j = i;
84 let mut k = hist_len - 1;
85 while j < k && history[j] == history[k] {
86 len += 1;
87 if breakers.contains(&history[j]) {
88 break;
89 }
90 if j == 0 {
91 break;
92 }
93 j -= 1;
94 k -= 1;
95 }
96 match_lengths[i] = len;
97 }
98
99 #[allow(clippy::needless_range_loop)]
100 for i in 0..hist_len.saturating_sub(1) {
101 let len = match_lengths[i];
102 if len > allowed {
103 let extend_pos = i + len;
104 if extend_pos < hist_len {
105 let token = history[extend_pos] as usize;
106 if token < n {
107 let penalty = multiplier * base.powi((len - allowed) as i32);
108 logits[token] -= penalty;
109 }
110 }
111 }
112 }
113}
114
115pub fn apply_penalties_and_bias(
124 logits: &mut [f32],
125 params: &SamplingParams,
126 token_history: &[u32],
127) {
128 let n = logits.len();
129
130 let rep_penalty = params.repetition_penalty;
134 if rep_penalty != 1.0 && rep_penalty > 0.0 && !token_history.is_empty() {
135 let window = params.repetition_penalty_window as usize;
136 let effective = if window > 0 && window < token_history.len() {
137 &token_history[token_history.len() - window..]
138 } else {
139 token_history
140 };
141 for &tid in effective {
142 if (tid as usize) < n {
143 let logit = &mut logits[tid as usize];
144 if *logit > 0.0 {
145 *logit /= rep_penalty;
146 } else {
147 *logit *= rep_penalty;
148 }
149 }
150 }
151 }
152
153 let freq_pen = params.frequency_penalty;
156 let pres_pen = params.presence_penalty;
157 if (freq_pen != 0.0 || pres_pen != 0.0) && !token_history.is_empty() {
158 let window = params.repetition_penalty_window as usize;
159 let effective = if window > 0 && window < token_history.len() {
160 &token_history[token_history.len() - window..]
161 } else {
162 token_history
163 };
164 let mut counts = std::collections::HashMap::<u32, u32>::new();
165 for &tid in effective {
166 *counts.entry(tid).or_insert(0) += 1;
167 }
168 for (&tid, &count) in &counts {
169 if (tid as usize) < n {
170 logits[tid as usize] -= freq_pen * count as f32 + pres_pen;
171 }
172 }
173 }
174
175 if params.lz_penalty > 0.0 && token_history.len() >= 4 {
176 apply_lz_penalty(logits, token_history, params.lz_penalty);
177 }
178
179 if params.dry_multiplier > 0.0 && token_history.len() >= 3 {
180 apply_dry_penalty(
181 logits,
182 token_history,
183 params.dry_multiplier,
184 params.dry_base,
185 params.dry_allowed_length,
186 ¶ms.dry_sequence_breakers,
187 );
188 }
189
190 for &(tid, bias) in ¶ms.logit_bias {
191 if (tid as usize) < n {
192 logits[tid as usize] += bias;
193 }
194 }
195}