1use std::marker::PhantomData;
2
3use super::*;
4use crate::tensor::{
5 View, ViewExpand, ViewOperations, ViewOperationsExpand,
6 launch::{ViewArg, ViewCompilationArg},
7 layout::Coordinates,
8};
9use cubecl::prelude::*;
10use cubecl_common::{
11 e2m1x2, e4m3, e5m2,
12 quant::scheme::{QuantParam, QuantScheme, QuantStore, QuantValue},
13 ue8m0,
14};
15use cubecl_core::{
16 self as cubecl, define_size,
17 ir::{ElemType, FloatKind, StorageType, VectorSize},
18 prelude::barrier::Barrier,
19 unexpanded,
20};
21use half::{bf16, f16};
22
23#[expect(dead_code, reason = "only used in expand")]
31#[derive(CubeType, CubeLaunch, Clone)]
32pub struct QuantizedView<
33 'a,
34 Q: Scalar,
35 NQ: Size,
36 S: Scalar,
37 F: Numeric,
38 NF: Size,
39 C: Coordinates + 'static,
40> {
41 values: View<'a, Vector<Q, NQ>, C>,
42 scales: View<'a, S, C>,
43 #[cube(comptime)]
44 scheme: QuantScheme,
45 #[cube(comptime)]
46 _ty: PhantomData<(F, NF)>,
47}
48
49#[cube]
50impl<'a, Q: Scalar, NQ: Size, S: Scalar, F: Numeric, NF: Size, C: Coordinates + 'static>
51 QuantizedView<'a, Q, NQ, S, F, NF, C>
52{
53 pub fn new(
54 values: View<'a, Vector<Q, NQ>, C>,
55 scales: View<'a, S, C>,
56 #[comptime] scheme: QuantScheme,
57 ) -> Self {
58 QuantizedView::<'a, Q, NQ, S, F, NF, C> {
59 values,
60 scales,
61 scheme,
62 _ty: PhantomData,
63 }
64 }
65}
66
67impl<'a, Q: Scalar, NQ: Size, S: Scalar, F: Numeric, NF: Size, C: Coordinates + 'static>
68 QuantizedView<'a, Q, NQ, S, F, NF, C>
69{
70 pub fn view(self) -> View<'a, Vector<F, NF>, C> {
71 unexpanded!()
72 }
73
74 pub fn __expand_view(
75 scope: &Scope,
76 this: QuantizedViewExpand<'a, Q, NQ, S, F, NF, C>,
77 ) -> ViewExpand<'a, Vector<F, NF>, C> {
78 this.__expand_view_method(scope)
79 }
80}
81
82impl<'a, Q: Scalar, NQ: Size, S: Scalar, F: Numeric, NF: Size, C: Coordinates + 'static>
83 QuantizedViewExpand<'a, Q, NQ, S, F, NF, C>
84{
85 pub fn new(
86 values: ViewExpand<'a, Vector<Q, NQ>, C>,
87 scales: ViewExpand<'a, S, C>,
88 scheme: QuantScheme,
89 ) -> Self {
90 QuantizedViewExpand::<'a, Q, NQ, S, F, NF, C> {
91 values,
92 scales,
93 scheme,
94 _ty: PhantomData,
95 }
96 }
97
98 pub fn __expand_view_method(self, scope: &Scope) -> ViewExpand<'a, Vector<F, NF>, C> {
99 ViewExpand::new(scope, self)
100 }
101}
102
103impl<'a, Q: Scalar, NQ: Size, S: Scalar, F: Numeric, NF: Size, C: Coordinates + 'static> Vectorized
104 for QuantizedView<'a, Q, NQ, S, F, NF, C>
105{
106}
107impl<'a, Q: Scalar, NQ: Size, S: Scalar, F: Numeric, NF: Size, C: Coordinates + 'static>
108 VectorizedExpand for QuantizedViewExpand<'a, Q, NQ, S, F, NF, C>
109{
110 fn vector_size(&self) -> VectorSize {
111 self.values.vector_size() * self.scheme.num_quants()
112 }
113}
114
115impl<'a, Q: Scalar, NQ: Size, S: Scalar, F: Numeric, NF: Size, C: Coordinates + 'static>
116 ViewOperations<Vector<F, NF>, C> for QuantizedView<'a, Q, NQ, S, F, NF, C>
117{
118}
119
120impl<'a, Q: Scalar, NQ: Size, S: Scalar, F: Numeric, NF: Size, C: Coordinates + 'static>
121 ViewOperationsExpand<Vector<F, NF>, C> for QuantizedViewExpand<'a, Q, NQ, S, F, NF, C>
122{
123 fn __expand_read_method(
124 &self,
125 scope: &Scope,
126 pos: <C>::ExpandType,
127 ) -> NativeExpand<Vector<F, NF>> {
128 let value = self.values.clone().__expand_read_method(scope, pos.clone());
129 let scale = self.scales.clone().__expand_read_method(scope, pos);
130
131 dequantize_aligned::expand::<Q, S, F, NQ, NF>(scope, value, scale, self.scheme)
132 }
133
134 fn __expand_read_checked_method(
135 &self,
136 scope: &Scope,
137 pos: <C>::ExpandType,
138 ) -> NativeExpand<Vector<F, NF>> {
139 let value = self
140 .values
141 .clone()
142 .__expand_read_checked_method(scope, pos.clone());
143 let scale = self
144 .scales
145 .clone()
146 .__expand_read_checked_method(scope, pos.clone());
147
148 dequantize_aligned::expand::<Q, S, F, NQ, NF>(scope, value, scale, self.scheme)
149 }
150
151 fn __expand_read_masked_method(
152 &self,
153 scope: &Scope,
154 pos: <C>::ExpandType,
155 mask_value: NativeExpand<Vector<F, NF>>,
156 ) -> NativeExpand<Vector<F, NF>> {
157 let value = self
158 .values
159 .clone()
160 .__expand_read_checked_method(scope, pos.clone());
161 let scale = self
162 .scales
163 .clone()
164 .__expand_read_checked_method(scope, pos.clone());
165 let in_bounds = self.__expand_is_in_bounds_method(scope, pos);
166
167 let value = dequantize_aligned::expand::<Q, S, F, NQ, NF>(scope, value, scale, self.scheme);
168 select::expand::<Vector<F, NF>>(scope, in_bounds, value, mask_value)
169 }
170
171 fn __expand_read_unchecked_method(
172 &self,
173 scope: &Scope,
174 pos: <C>::ExpandType,
175 ) -> NativeExpand<Vector<F, NF>> {
176 let value = self
177 .values
178 .clone()
179 .__expand_read_unchecked_method(scope, pos.clone());
180 let scale = self
181 .scales
182 .clone()
183 .__expand_read_unchecked_method(scope, pos);
184
185 dequantize_aligned::expand::<Q, S, F, NQ, NF>(scope, value, scale, self.scheme)
186 }
187
188 fn __expand_as_linear_slice_method(
189 &self,
190 _scope: &Scope,
191 _pos: <C>::ExpandType,
192 _end: <C>::ExpandType,
193 ) -> &SliceExpand<Vector<F, NF>> {
194 panic!("Can't create raw slice for quantized view")
195 }
196
197 fn __expand_shape_method(&self, scope: &Scope) -> <C>::ExpandType {
198 self.values.clone().__expand_shape_method(scope)
199 }
200
201 fn __expand_is_in_bounds_method(
202 &self,
203 scope: &Scope,
204 pos: C::ExpandType,
205 ) -> NativeExpand<bool> {
206 self.values.clone().__expand_is_in_bounds_method(scope, pos)
207 }
208
209 fn __expand_tensor_map_load_method(
210 &self,
211 _scope: &Scope,
212 _barrier: &NativeExpand<Barrier>,
213 _shared_memory: &mut SliceExpand<Vector<F, NF>>,
214 _pos: C::ExpandType,
215 ) {
216 panic!("Can't use tensor map functions on quantized view");
217 }
218}
219
220fn quant_vector_size_q(vector_size: usize, num_quants: usize) -> usize {
223 assert!(
224 vector_size >= num_quants && vector_size.is_multiple_of(num_quants),
225 "quantized view float vector size {vector_size} must be a positive multiple of num_quants {num_quants}"
226 );
227 vector_size / num_quants
228}
229
230struct ExpandDynamic<'a, E: Numeric, N: Size, C: Coordinates + 'static> {
231 values: &'a ViewCompilationArg<C>,
232 scales: &'a ViewCompilationArg<C>,
233 scheme: QuantScheme,
234 builder: &'a mut KernelBuilder,
235 _ty: PhantomData<(E, N)>,
236}
237
238impl<'a, E: Numeric, N: Size, C: Coordinates + 'static> RunWithQuantType
239 for ExpandDynamic<'a, E, N, C>
240{
241 type Output = ViewExpand<'static, Vector<E, N>, C>;
242
243 fn execute<Q: Scalar, S: Scalar>(self) -> Self::Output {
244 define_size!(NQ);
245
246 let vector_size = N::__expand_value(&self.builder.scope);
247 let vector_size_q = quant_vector_size_q(vector_size, self.scheme.num_quants());
248 self.builder.scope.register_size::<NQ>(vector_size_q);
249
250 let values = View::<Vector<Q, NQ>, C>::expand(self.values, self.builder);
251 let scales = View::<S, C>::expand(self.scales, self.builder);
252 let view = QuantizedViewExpand::new(values, scales, self.scheme);
253 ViewExpand::new(&self.builder.scope, view)
254 }
255}
256
257pub(crate) struct RegisterDynamic<'a, E: CubePrimitive, C: Coordinates + 'static, R: Runtime> {
258 pub values: ViewArg<C, R>,
259 pub scales: ViewArg<C, R>,
260 pub scheme: QuantScheme,
261 pub launcher: &'a mut KernelLauncher<R>,
262 pub _ty: PhantomData<E>,
263}
264
265impl<'a, E: CubePrimitive, C: Coordinates + 'static, R: Runtime> RunWithQuantType
266 for RegisterDynamic<'a, E, C, R>
267{
268 type Output = ViewCompilationArg<C>;
269
270 fn execute<Q: Scalar, S: Scalar>(self) -> Self::Output {
271 define_size!(NQ);
272
273 self.launcher.with_scope(|scope| {
274 let vector_size_q =
275 quant_vector_size_q(E::__expand_vector_size(scope), self.scheme.num_quants());
276 scope.register_size::<NQ>(vector_size_q);
277 });
278
279 let values = View::<Vector<Q, NQ>, C>::register(self.values, self.launcher);
280 let scales = View::<S, C>::register(self.scales, self.launcher);
281 ViewCompilationArg::Quantized {
282 values: Box::new(values),
283 scales: Box::new(scales),
284 scheme: self.scheme,
285 }
286 }
287}
288
289pub fn run_with_quant_type<F: RunWithQuantType>(func: F, scheme: QuantScheme) -> F::Output {
292 assert_level_supported(scheme.level);
295
296 fn run_with_q<F: RunWithQuantType, Q: Scalar>(func: F, scheme: QuantScheme) -> F::Output {
297 match scheme.param {
298 QuantParam::F32 => func.execute::<Q, f32>(),
299 QuantParam::F16 => func.execute::<Q, f16>(),
300 QuantParam::BF16 => func.execute::<Q, bf16>(),
301 QuantParam::UE8M0 => func.execute::<Q, ue8m0>(),
302 QuantParam::UE4M3 => func.execute::<Q, e4m3>(),
303 }
304 }
305
306 let run_q = match scheme.store {
307 QuantStore::Native => match scheme.value {
308 QuantValue::Q8F => run_with_q::<F, i8>,
309 QuantValue::Q8S => run_with_q::<F, i8>,
310 QuantValue::E5M2 => run_with_q::<F, e5m2>,
311 QuantValue::E4M3 => run_with_q::<F, e4m3>,
312 QuantValue::Q4F
313 | QuantValue::Q4S
314 | QuantValue::Q2F
315 | QuantValue::Q2S
316 | QuantValue::E2M1 => {
317 panic!("Sub-byte quantization can't be native")
318 }
319 },
320 QuantStore::PackedU32(_) => run_with_q::<F, u32>,
321 QuantStore::PackedNative(_) => run_with_q::<F, e2m1x2>,
322 };
323 run_q(func, scheme)
324}
325
326pub(crate) fn expand_dynamic<E: CubePrimitive, C: Coordinates + 'static>(
329 values: &ViewCompilationArg<C>,
330 scales: &ViewCompilationArg<C>,
331 scheme: QuantScheme,
332 builder: &mut KernelBuilder,
333) -> ViewExpand<'static, E, C> {
334 use core::mem::transmute as t;
335
336 fn expand_dynamic_f<F: Numeric, NF: Size, C: Coordinates + 'static>(
338 values: &ViewCompilationArg<C>,
339 scales: &ViewCompilationArg<C>,
340 scheme: QuantScheme,
341 builder: &mut KernelBuilder,
342 ) -> ViewExpand<'static, Vector<F, NF>, C> {
343 let func = ExpandDynamic {
344 values,
345 scales,
346 scheme,
347 builder,
348 _ty: PhantomData::<(F, NF)>,
349 };
350 run_with_quant_type(func, scheme)
351 }
352
353 define_size!(NF);
354
355 let vector_size = E::__expand_vector_size(&builder.scope);
356
357 builder.scope.register_size::<NF>(vector_size);
358
359 #[allow(clippy::missing_transmute_annotations)]
360 unsafe {
361 match E::__expand_as_type(&builder.scope).storage_type() {
362 StorageType::Scalar(ElemType::Float(ty)) => match ty {
363 FloatKind::F16 => t(expand_dynamic_f::<f16, NF, C>(
364 values, scales, scheme, builder,
365 )),
366 FloatKind::BF16 => t(expand_dynamic_f::<bf16, NF, C>(
367 values, scales, scheme, builder,
368 )),
369 FloatKind::Flex32 => t(expand_dynamic_f::<flex32, NF, C>(
370 values, scales, scheme, builder,
371 )),
372 FloatKind::F32 => t(expand_dynamic_f::<f32, NF, C>(
373 values, scales, scheme, builder,
374 )),
375 FloatKind::TF32 => t(expand_dynamic_f::<tf32, NF, C>(
376 values, scales, scheme, builder,
377 )),
378 FloatKind::F64 => t(expand_dynamic_f::<f64, NF, C>(
379 values, scales, scheme, builder,
380 )),
381 FloatKind::E2M1
382 | FloatKind::E2M3
383 | FloatKind::E3M2
384 | FloatKind::E4M3
385 | FloatKind::E5M2
386 | FloatKind::UE8M0 => unreachable!("Minifloats don't implement `Float` ops"),
387 },
388 _ => unreachable!("Quantized view should only be used with floats"),
389 }
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::{RunWithQuantType, quant_vector_size_q, run_with_quant_type};
396 use cubecl_common::quant::scheme::{QuantLevel, QuantParam, QuantScheme};
397 use cubecl_core::prelude::Scalar;
398
399 struct Dispatched;
400
401 impl RunWithQuantType for Dispatched {
402 type Output = bool;
403
404 fn execute<Q: Scalar, S: Scalar>(self) -> bool {
405 true
406 }
407 }
408
409 #[test]
410 fn one_level_scheme_dispatches() {
411 assert!(run_with_quant_type(Dispatched, QuantScheme::default()));
412 }
413
414 #[test]
415 #[should_panic(expected = "two-level quantization is not supported")]
416 fn two_level_scheme_is_rejected() {
417 let scheme =
418 QuantScheme::default().with_level(QuantLevel::block_tensor([32], QuantParam::F32));
419 run_with_quant_type(Dispatched, scheme);
421 }
422
423 #[test]
424 fn vector_size_q_exact_multiple() {
425 assert_eq!(quant_vector_size_q(8, 8), 1);
426 assert_eq!(quant_vector_size_q(16, 8), 2);
427 assert_eq!(quant_vector_size_q(16, 16), 1);
428 }
429
430 #[test]
431 #[should_panic(expected = "positive multiple of num_quants")]
432 fn vector_size_q_non_multiple_panics() {
433 let _ = quant_vector_size_q(8, 16);
434 }
435}