Skip to main content

fuel_quantized/
utils.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2pub(super) fn nearest_int(v: f32) -> i32 {
3    v.round() as i32
4}
5
6/// Validates that the input and output are the right size and returns an iterator which maps each
7/// input region `xs` to its corresponding output block in `ys`. Each output region is guaranteed
8/// to be `T::BLCK_SIZE` long.
9pub(super) fn group_for_quantization<'a, 'b, T: super::k_quants::GgmlType>(
10    xs: &'b [f32],
11    ys: &'a mut [T],
12) -> Vec<(&'a mut T, &'b [f32])> {
13    let block_size = T::BLCK_SIZE;
14    let dtype = T::DTYPE;
15
16    let expected_blocks = xs.len() / block_size;
17    let actual_blocks = ys.len();
18
19    // Validate that the input is the right size
20    debug_assert_eq!(
21        expected_blocks, actual_blocks,
22        "quantize {dtype:?}: expected {expected_blocks} blocks but only {actual_blocks} were provided!"
23    );
24
25    ys.iter_mut().zip(xs.chunks_exact(block_size)).collect()
26}
27
28/// Validates that the input and output are the right size and returns an iterator which maps each
29/// input block `xs` to its corresponding output region in `ys`. Each output region is guaranteed
30/// to be `T::BLCK_SIZE` long.
31pub(super) fn group_for_dequantization<'a, 'b, T: super::k_quants::GgmlType>(
32    xs: &'a [T],
33    ys: &'b mut [f32],
34) -> Vec<(&'a T, &'b mut [f32])> {
35    let block_size = T::BLCK_SIZE;
36    let dtype = T::DTYPE;
37
38    let actual_output_len = ys.len();
39    let expected_output_len = xs.len() * block_size;
40    // Validate that the output is the right size
41    debug_assert_eq!(
42        expected_output_len, actual_output_len,
43        "dequantize {dtype:?}: ys (len = {actual_output_len}) does not match the expected length of {expected_output_len}!"
44    );
45
46    // Zip the blocks and outputs together
47    xs.iter().zip(ys.chunks_exact_mut(block_size)).collect()
48}
49
50pub(super) fn get_scale_min_k4(j: usize, q: &[u8]) -> (u8, u8) {
51    if j < 4 {
52        let d = q[j] & 63;
53        let m = q[j + 4] & 63;
54        (d, m)
55    } else {
56        let d = (q[j + 4] & 0xF) | ((q[j - 4] >> 6) << 4);
57        let m = (q[j + 4] >> 4) | ((q[j] >> 6) << 4);
58        (d, m)
59    }
60}
61
62pub(super) unsafe fn make_qx_quants(
63    n: usize,
64    nmax: i32,
65    x: *const f32,
66    ls: *mut i8,
67    rmse_type: i32,
68    qw: *const f32,
69) -> f32 {
70    unsafe {
71        let mut max = 0f32;
72        let mut amax = 0f32;
73        for i in 0..n {
74            let x = *x.add(i);
75            let ax = x.abs();
76            if ax > amax {
77                amax = ax;
78                max = x;
79            }
80        }
81        if amax == 0. {
82            // all zero
83            for i in 0..n {
84                *ls.add(i) = 0;
85            }
86            return 0.;
87        }
88        let mut iscale = -(nmax as f32) / max;
89        if rmse_type == 0 {
90            for i in 0..n {
91                let x = *x.add(i);
92                let l = nearest_int(iscale * x);
93                *ls.add(i) = (nmax + l.clamp(-nmax, nmax - 1)) as i8;
94            }
95            return 1.0 / iscale;
96        }
97        let weight_type = rmse_type % 2;
98        let mut sumlx = 0f32;
99        let mut suml2 = 0f32;
100        for i in 0..n {
101            let x = *x.add(i);
102            let l = nearest_int(iscale * x);
103            let l = l.clamp(-nmax, nmax - 1);
104            *ls.add(i) = (l + nmax) as i8;
105            let w = if !qw.is_null() {
106                *qw.add(i)
107            } else if weight_type == 1 {
108                x * x
109            } else {
110                1.0
111            };
112            let l = l as f32;
113            sumlx += w * x * l;
114            suml2 += w * l * l;
115        }
116        let mut scale = sumlx / suml2;
117        let mut best = scale * sumlx;
118        for _itry in 0..3 {
119            let iscale = 1.0 / scale;
120            let mut slx = 0f32;
121            let mut sl2 = 0f32;
122            let mut changed = false;
123            for i in 0..n {
124                let x = *x.add(i);
125                let l = nearest_int(iscale * x);
126                let l = l.clamp(-nmax, nmax - 1);
127                if l + nmax != *ls.add(i) as i32 {
128                    changed = true;
129                }
130                let w = if !qw.is_null() {
131                    *qw.add(i)
132                } else if weight_type == 1 {
133                    x * x
134                } else {
135                    1.0
136                };
137                let l = l as f32;
138                slx += w * x * l;
139                sl2 += w * l * l;
140            }
141            if !changed || sl2 == 0.0 || slx * slx <= best * sl2 {
142                break;
143            }
144            for i in 0..n {
145                let x = *x.add(i);
146                let l = nearest_int(iscale * x);
147                *ls.add(i) = (nmax + l.clamp(-nmax, nmax - 1)) as i8;
148            }
149            sumlx = slx;
150            suml2 = sl2;
151            scale = sumlx / suml2;
152            best = scale * sumlx;
153        }
154        for _itry in 0..5 {
155            let mut n_changed = 0;
156            for i in 0..n {
157                let x = *x.add(i);
158                let w = if !qw.is_null() {
159                    *qw.add(i)
160                } else if weight_type == 1 {
161                    x * x
162                } else {
163                    1.0
164                };
165                let l = *ls.add(i) as i32 - nmax;
166                let mut slx = sumlx - w * x * l as f32;
167                if slx > 0. {
168                    let mut sl2 = suml2 - w * l as f32 * l as f32;
169                    let new_l = nearest_int(x * sl2 / slx);
170                    let new_l = new_l.clamp(-nmax, nmax - 1);
171                    if new_l != l {
172                        slx += w * x * new_l as f32;
173                        sl2 += w * new_l as f32 * new_l as f32;
174                        if sl2 > 0. && slx * slx * suml2 > sumlx * sumlx * sl2 {
175                            *ls.add(i) = (nmax + new_l) as i8;
176                            sumlx = slx;
177                            suml2 = sl2;
178                            scale = sumlx / suml2;
179                            best = scale * sumlx;
180                            n_changed += 1;
181                        }
182                    }
183                }
184            }
185            if n_changed == 0 {
186                break;
187            }
188        }
189        if rmse_type < 3 {
190            return scale;
191        }
192        for is in -4..4 {
193            if is == 0 {
194                continue;
195            }
196            iscale = -(nmax as f32 + 0.1f32 * is as f32) / max;
197            let mut sumlx = 0.;
198            let mut suml2 = 0.;
199            for i in 0..n {
200                let x = *x.add(i);
201                let l = nearest_int(iscale * x);
202                let l = l.clamp(-nmax, nmax - 1);
203                let w = if !qw.is_null() {
204                    *qw.add(i)
205                } else if weight_type == 1 {
206                    x * x
207                } else {
208                    1.0
209                };
210                let l = l as f32;
211                sumlx += w * x * l;
212                suml2 += w * l * l;
213            }
214            if suml2 > 0. && sumlx * sumlx > best * suml2 {
215                for i in 0..n {
216                    let x = *x.add(i);
217                    let l = nearest_int(iscale * x);
218                    *ls.add(i) = (nmax + l.clamp(-nmax, nmax - 1)) as i8;
219                }
220                scale = sumlx / suml2;
221                best = scale * sumlx;
222            }
223        }
224        scale
225    }
226}
227
228// https://github.com/ggerganov/llama.cpp/blob/8183159cf3def112f6d1fe94815fce70e1bffa12/k_quants.c#L224
229pub(super) fn make_qkx1_quants(nmax: i32, ntry: usize, x: &[f32]) -> (f32, f32) {
230    let n = x.len();
231    let mut l = vec![0; n];
232    // Get min/max
233    let min = *x
234        .iter()
235        .take(n)
236        .min_by(|a, b| a.total_cmp(b))
237        .unwrap_or(&x[0]);
238    let max = *x.iter().max_by(|a, b| a.total_cmp(b)).unwrap_or(&x[0]);
239
240    // If min == max, all values are the same => nothing to do here
241    if max == min {
242        return (0.0, 0.0);
243    }
244
245    // Ensure min <= 0.0
246    let mut min = min.min(0.);
247
248    // Compute scale and inverse scale
249    let mut iscale = nmax as f32 / (max - min);
250    let mut scale = 1.0 / iscale;
251
252    for _ in 0..ntry {
253        let mut sumlx = 0.0;
254        let mut suml2 = 0;
255        let mut did_change = false;
256
257        for (i, value) in x.iter().enumerate().take(n) {
258            let li = nearest_int(iscale * (value - min)).clamp(0, nmax);
259            let clamped_li = li as u8;
260            if clamped_li != l[i] {
261                l[i] = clamped_li;
262                did_change = true;
263            }
264            sumlx += (value - min) * li as f32;
265            suml2 += li * li;
266        }
267        scale = sumlx / suml2 as f32;
268
269        let sum: f32 = x
270            .iter()
271            .take(n)
272            .zip(l.iter().take(n))
273            .map(|(xi, &li)| xi - scale * li as f32)
274            .sum();
275
276        min = sum / n as f32;
277        if min > 0.0 {
278            min = 0.0;
279        }
280        iscale = 1.0 / scale;
281        if !did_change {
282            break;
283        }
284    }
285    (scale, -min)
286}
287
288// https://github.com/ggerganov/llama.cpp/blob/8183159cf3def112f6d1fe94815fce70e1bffa12/k_quants.c#L165
289pub(super) fn make_q3_quants(x: &[f32], nmax: i32, do_rmse: bool) -> f32 {
290    let n = x.len();
291    let mut l = vec![0i8; n];
292
293    let mut max = 0.0;
294    let mut amax = 0.0;
295    for &xi in x.iter().take(n) {
296        let ax = xi.abs();
297        if ax > amax {
298            amax = ax;
299            max = xi;
300        }
301    }
302
303    if amax == 0.0 {
304        return 0.0;
305    }
306
307    let iscale = -(nmax as f32) / max;
308    if do_rmse {
309        let mut sumlx = 0.0;
310        let mut suml2 = 0.0;
311        for i in 0..n {
312            let li = (iscale * x[i]).round() as i32;
313            let li = li.clamp(-nmax, nmax - 1);
314            l[i] = li as i8;
315            let w = x[i] * x[i];
316            sumlx += w * x[i] * li as f32;
317            suml2 += w * (li * li) as f32;
318        }
319        for _ in 0..5 {
320            let mut n_changed = 0;
321            for i in 0..n {
322                let w = x[i] * x[i];
323                let mut slx = sumlx - w * x[i] * l[i] as f32;
324                if slx > 0.0 {
325                    let mut sl2 = suml2 - w * (l[i] as i32 * l[i] as i32) as f32;
326                    let mut new_l = (x[i] * sl2 / slx).round() as i32;
327                    new_l = new_l.clamp(-nmax, nmax - 1);
328                    if new_l != l[i] as i32 {
329                        slx += w * x[i] * new_l as f32;
330                        sl2 += w * (new_l * new_l) as f32;
331                        if sl2 > 0.0 && slx * slx * suml2 > sumlx * sumlx * sl2 {
332                            l[i] = new_l as i8;
333                            sumlx = slx;
334                            suml2 = sl2;
335                            n_changed += 1;
336                        }
337                    }
338                }
339            }
340            if n_changed == 0 {
341                break;
342            }
343        }
344        for li in l.iter_mut() {
345            *li += nmax as i8;
346        }
347        return sumlx / suml2;
348    }
349    for i in 0..n {
350        let li = (iscale * x[i]).round() as i32;
351        l[i] = (li.clamp(-nmax, nmax - 1) + nmax) as i8;
352    }
353    1.0 / iscale
354}
355
356// https://github.com/ggerganov/llama.cpp/blob/678d7994f4da0af3d29046be99950ac999ee9762/ggml/src/ggml-quants.c#L744
357/// (scale, min)
358pub(super) fn make_qkx3_quants(
359    nmax: i32,
360    x: &[f32],
361    weights: Option<&[f32]>,
362    rmin: f32,
363    rdelta: f32,
364    nstep: usize,
365    use_mad: bool,
366) -> (f32, f32) {
367    let n = x.len();
368    let mut l: [u8; 32] = [0; 32];
369    let mut l_aux: [u8; 32] = [0; 32];
370
371    let mut min_val = x[0];
372    let mut max_val = x[0];
373    let mut sum_w = match weights {
374        Some(w) => w[0],
375        None => x[0] * x[0],
376    };
377    let mut sum_x = sum_w * x[0];
378
379    for i in 1..n {
380        if x[i] < min_val {
381            min_val = x[i];
382        }
383        if x[i] > max_val {
384            max_val = x[i];
385        }
386        let w = match weights {
387            Some(w) => w[i],
388            None => x[i] * x[i],
389        };
390        sum_w += w;
391        sum_x += w * x[i];
392    }
393
394    if min_val > 0.0 {
395        min_val = 0.0;
396    }
397
398    if max_val <= min_val {
399        return (0.0, -min_val);
400    }
401
402    let mut iscale = nmax as f32 / (max_val - min_val);
403    let mut scale = 1.0 / iscale;
404    let mut best_mad = 0.0;
405
406    for i in 0..n {
407        let l_val = nearest_int(iscale * (x[i] - min_val)).clamp(0, nmax) as u8;
408        l[i] = l_val;
409        let diff = scale * (l_val as f32) + min_val - x[i];
410        let diff = if use_mad { diff.abs() } else { diff * diff };
411        let w = match weights {
412            Some(w) => w[i],
413            None => x[i] * x[i],
414        };
415        best_mad += w * diff;
416    }
417
418    if nstep < 1 {
419        return (scale, -min_val);
420    }
421
422    for is in 0..=nstep {
423        iscale = (rmin + rdelta * is as f32 + nmax as f32) / (max_val - min_val);
424        let (mut sum_l, mut sum_l2, mut sum_xl) = (0.0, 0.0, 0.0);
425
426        for i in 0..n {
427            let l_val = nearest_int(iscale * (x[i] - min_val)).clamp(0, nmax) as u8;
428            l_aux[i] = l_val;
429            let w = match weights {
430                Some(w) => w[i],
431                None => x[i] * x[i],
432            };
433            sum_l += w * l_val as f32;
434            sum_l2 += w * (l_val as f32).powi(2);
435            sum_xl += w * l_val as f32 * x[i];
436        }
437
438        let d = sum_w * sum_l2 - sum_l * sum_l;
439        if d > 0.0 {
440            let mut this_scale = (sum_w * sum_xl - sum_x * sum_l) / d;
441            let mut this_min = (sum_l2 * sum_x - sum_l * sum_xl) / d;
442
443            if this_min > 0.0 {
444                this_min = 0.0;
445                this_scale = sum_xl / sum_l2;
446            }
447
448            let mut mad = 0.0;
449            for i in 0..n {
450                let diff = this_scale * (l_aux[i] as f32) + this_min - x[i];
451                let diff = if use_mad { diff.abs() } else { diff * diff };
452                let w = match weights {
453                    Some(w) => w[i],
454                    None => x[i] * x[i],
455                };
456                mad += w * diff;
457            }
458
459            if mad < best_mad {
460                l.copy_from_slice(&l_aux);
461                best_mad = mad;
462                scale = this_scale;
463                min_val = this_min;
464            }
465        }
466    }
467
468    (scale, -min_val)
469}
470
471// https://github.com/ggerganov/llama.cpp/blob/678d7994f4da0af3d29046be99950ac999ee9762/ggml/src/ggml-quants.c#L827
472pub(super) fn make_qp_quants(
473    n: usize,
474    nmax: u8,
475    x: &[f32],
476    l: &mut [u8],
477    quant_weights: &[f32],
478) -> f32 {
479    assert_eq!(x.len(), n);
480    assert_eq!(l.len(), n);
481    assert_eq!(quant_weights.len(), n);
482
483    let max = x.iter().copied().fold(0.0, f32::max);
484    if max == 0.0 {
485        l.iter_mut().for_each(|li| *li = 0);
486        return 0.0;
487    }
488
489    let mut iscale = nmax as f32 / max;
490    for (xi, li) in x.iter().zip(l.iter_mut()) {
491        *li = nearest_int(iscale * xi) as u8;
492    }
493
494    let scale = 1.0 / iscale;
495    let mut best_mse = x
496        .iter()
497        .zip(l.iter())
498        .zip(quant_weights.iter())
499        .map(|((&xi, &li), &w)| {
500            let diff = xi - scale * li as f32;
501            w * diff * diff
502        })
503        .sum::<f32>();
504
505    for is in -4..=4 {
506        if is == 0 {
507            continue;
508        }
509        let iscale_is = (0.1 * is as f32 + nmax as f32) / max;
510        let scale_is = 1.0 / iscale_is;
511
512        let mse = x
513            .iter()
514            .zip(quant_weights.iter())
515            .map(|(&xi, &w)| {
516                let mut li = nearest_int(iscale_is * xi) as u8;
517                li = li.min(nmax);
518                let diff = xi - scale_is * li as f32;
519                w * diff * diff
520            })
521            .sum::<f32>();
522
523        if mse < best_mse {
524            best_mse = mse;
525            iscale = iscale_is;
526        }
527    }
528
529    let mut sumlx = 0.0;
530    let mut suml2 = 0.0;
531    for ((xi, li), &w) in x.iter().zip(l.iter_mut()).zip(quant_weights.iter()) {
532        let mut li_new = (iscale * xi).round() as u8;
533        li_new = li_new.min(nmax);
534        *li = li_new;
535        sumlx += w * xi * li_new as f32;
536        suml2 += w * (li_new as f32).powi(2);
537    }
538
539    for _ in 0..5 {
540        let mut n_changed = 0;
541        for ((xi, li), &w) in x.iter().zip(l.iter_mut()).zip(quant_weights.iter()) {
542            let mut slx = sumlx - w * xi * *li as f32;
543            let mut sl2 = suml2 - w * (*li as f32).powi(2);
544            if slx > 0.0 && sl2 > 0.0 {
545                let new_li = (nearest_int(xi * sl2 / slx) as u8).min(nmax);
546                if new_li != *li {
547                    slx += w * xi * new_li as f32;
548                    sl2 += w * (new_li as f32).powi(2);
549                    if slx.powi(2) * suml2 > sumlx.powi(2) * sl2 {
550                        *li = new_li;
551                        sumlx = slx;
552                        suml2 = sl2;
553                        n_changed += 1;
554                    }
555                }
556            }
557        }
558        if n_changed == 0 {
559            break;
560        }
561    }
562
563    sumlx / suml2
564}