1use crate::{
2 backend::BackendStorage, CpuStorage, DType, Device, Result, Shape, Storage, Tensor, D,
3};
4use iq_quants::*;
5use k_quants::*;
6use std::borrow::Cow;
7use std::sync::Arc;
8
9#[cfg(target_feature = "avx2")]
10pub mod avx;
11pub mod dsv4_qat;
12mod dummy_cuda;
13mod dummy_metal;
14pub mod expert_stream;
15pub mod ggml_file;
16pub mod gguf_file;
17pub mod imatrix_file;
18mod iq_grids;
19pub mod iq_quants;
20pub mod k_quants;
21#[cfg(feature = "metal")]
22pub mod metal;
23#[cfg(not(target_arch = "wasm32"))]
24pub mod tokenizer;
25#[cfg(not(feature = "metal"))]
26mod metal {
27 pub use super::dummy_metal::*;
28}
29#[cfg(feature = "cuda")]
30pub mod cuda;
31#[cfg(feature = "cuda")]
32pub mod fast_mmq;
33#[cfg(feature = "cuda")]
34pub mod fast_mmvq;
35#[cfg(not(feature = "cuda"))]
36mod cuda {
37 pub use super::dummy_cuda::*;
38}
39
40#[cfg(target_feature = "neon")]
41pub mod neon;
42#[cfg(target_feature = "simd128")]
43pub mod simd128;
44pub mod utils;
45pub mod quant_format;
48use half::{bf16, f16};
49
50pub use k_quants::GgmlType;
51
52fn as_t_slice<T>(data: &[u8]) -> &[T] {
56 let size = std::mem::size_of::<T>();
57 assert_eq!(
58 data.len() % size,
59 0,
60 "Data length must be a multiple of T's size"
61 );
62 let ptr = data.as_ptr();
63 assert_eq!(
64 (ptr as usize) % std::mem::align_of::<T>(),
65 0,
66 "Data pointer must be aligned to T's alignment"
67 );
68 unsafe { std::slice::from_raw_parts(ptr as *const T, data.len() / size) }
69}
70
71#[derive(Default)]
76struct ResidentBanks {
77 #[cfg(feature = "rocm")]
78 rocm: std::sync::OnceLock<std::sync::Arc<crate::RocmStorage>>,
79 #[cfg(feature = "vulkan")]
80 vulkan: std::sync::OnceLock<std::sync::Arc<crate::VulkanStorage>>,
81 #[cfg(feature = "vulkan")]
82 vulkan_split: std::sync::OnceLock<std::sync::Arc<crate::vulkan::MoeBankSplit>>,
83 #[cfg(feature = "wgpu")]
84 wgpu: std::sync::OnceLock<std::sync::Arc<crate::WgpuStorage>>,
85}
86
87pub struct QTensor {
88 storage: QStorage,
89 shape: Shape,
90 #[cfg_attr(
97 not(any(feature = "rocm", feature = "vulkan", feature = "wgpu")),
98 allow(dead_code)
99 )]
100 banks: ResidentBanks,
101}
102
103impl Device {
104 fn qzeros(&self, elem_count: usize, dtype: GgmlDType) -> Result<QStorage> {
105 match self {
106 Device::Cpu => {
107 let storage = dtype.cpu_zeros(elem_count);
108 Ok(QStorage::Cpu(storage))
109 }
110 Device::Metal(metal) => {
111 let storage = metal::QMetalStorage::zeros(metal, elem_count, dtype)?;
112 Ok(QStorage::Metal(storage))
113 }
114 Device::Cuda(cuda) => {
115 let storage = cuda::QCudaStorage::zeros(cuda, elem_count, dtype)?;
116 Ok(QStorage::Cuda(storage))
117 }
118 #[cfg(feature = "rocm")]
119 Device::Rocm(d) => {
120 let storage = dtype.cpu_zeros(elem_count);
123 Ok(QStorage::Rocm(storage, d.clone()))
124 }
125 #[cfg(feature = "vulkan")]
126 Device::Vulkan(d) => {
127 let storage = dtype.cpu_zeros(elem_count);
130 Ok(QStorage::Vulkan(storage, d.clone()))
131 }
132 #[cfg(feature = "wgpu")]
133 Device::Wgpu(d) => {
134 let storage = dtype.cpu_zeros(elem_count);
137 Ok(QStorage::Wgpu(storage, d.clone()))
138 }
139 }
140 }
141}
142
143pub enum QStorage {
144 Cpu(Box<dyn QuantizedType>),
145 Metal(metal::QMetalStorage),
146 Cuda(cuda::QCudaStorage),
147 #[cfg(feature = "rocm")]
151 Rocm(Box<dyn QuantizedType>, crate::RocmDevice),
152 #[cfg(feature = "vulkan")]
156 Vulkan(Box<dyn QuantizedType>, crate::VulkanDevice),
157 #[cfg(feature = "wgpu")]
161 Wgpu(Box<dyn QuantizedType>, crate::WgpuDevice),
162 Stream(Arc<expert_stream::ExpertStreamBank>),
166}
167
168impl QStorage {
169 pub fn from_data(data: Cow<'_, [u8]>, device: &Device, dtype: GgmlDType) -> Result<Self> {
170 match device {
171 Device::Cpu => Ok(Self::Cpu(dtype.from_data(data))),
172 Device::Metal(d) => match dtype {
173 GgmlDType::F32 => metal::load_quantized(d, as_t_slice::<f32>(&data)),
174 GgmlDType::F16 => metal::load_quantized(d, as_t_slice::<f16>(&data)),
175 GgmlDType::Q4_0 => metal::load_quantized(d, as_t_slice::<BlockQ4_0>(&data)),
176 GgmlDType::Q4_1 => metal::load_quantized(d, as_t_slice::<BlockQ4_1>(&data)),
177 GgmlDType::Q5_0 => metal::load_quantized(d, as_t_slice::<BlockQ5_0>(&data)),
178 GgmlDType::Q5_1 => metal::load_quantized(d, as_t_slice::<BlockQ5_1>(&data)),
179 GgmlDType::Q8_0 => metal::load_quantized(d, as_t_slice::<BlockQ8_0>(&data)),
180 GgmlDType::Q8_1 => metal::load_quantized(d, as_t_slice::<BlockQ8_1>(&data)),
181 GgmlDType::Q2K => metal::load_quantized(d, as_t_slice::<BlockQ2K>(&data)),
182 GgmlDType::Q3K => metal::load_quantized(d, as_t_slice::<BlockQ3K>(&data)),
183 GgmlDType::Q4K => metal::load_quantized(d, as_t_slice::<BlockQ4K>(&data)),
184 GgmlDType::Q5K => metal::load_quantized(d, as_t_slice::<BlockQ5K>(&data)),
185 GgmlDType::Q6K => metal::load_quantized(d, as_t_slice::<BlockQ6K>(&data)),
186 GgmlDType::Q8K => metal::load_quantized(d, as_t_slice::<BlockQ8K>(&data)),
187 GgmlDType::IQ4_NL => metal::load_quantized(d, as_t_slice::<BlockIQ4nl>(&data)),
188 GgmlDType::IQ4_XS => metal::load_quantized(d, as_t_slice::<BlockIQ4xs>(&data)),
189 GgmlDType::MXFP4 => metal::load_quantized(d, as_t_slice::<BlockMXFP4>(&data)),
190 GgmlDType::BF16 => metal::load_quantized(d, as_t_slice::<bf16>(&data)),
191 GgmlDType::I32 => metal::load_quantized(d, as_t_slice::<i32>(&data)),
192 GgmlDType::IQ2_XXS => metal::load_quantized(d, as_t_slice::<BlockIQ2xxs>(&data)),
198 GgmlDType::IQ2_XS => metal::load_quantized(d, as_t_slice::<BlockIQ2xs>(&data)),
199 GgmlDType::IQ2_S => metal::load_quantized(d, as_t_slice::<BlockIQ2s>(&data)),
200 GgmlDType::IQ3_XXS => metal::load_quantized(d, as_t_slice::<BlockIQ3xxs>(&data)),
201 GgmlDType::IQ3_S => metal::load_quantized(d, as_t_slice::<BlockIQ3s>(&data)),
202 GgmlDType::IQ1_S => metal::load_quantized(d, as_t_slice::<BlockIQ1s>(&data)),
203 GgmlDType::IQ1_M => metal::load_quantized(d, as_t_slice::<BlockIQ1m>(&data)),
204 other => crate::bail!("{other:?} is not supported on the Metal backend"),
206 },
207 Device::Cuda(d) => match dtype {
208 GgmlDType::F32 => cuda::load_quantized(d, as_t_slice::<f32>(&data)),
209 GgmlDType::F16 => cuda::load_quantized(d, as_t_slice::<f16>(&data)),
210 GgmlDType::Q4_0 => cuda::load_quantized(d, as_t_slice::<BlockQ4_0>(&data)),
211 GgmlDType::Q4_1 => cuda::load_quantized(d, as_t_slice::<BlockQ4_1>(&data)),
212 GgmlDType::Q5_0 => cuda::load_quantized(d, as_t_slice::<BlockQ5_0>(&data)),
213 GgmlDType::Q5_1 => cuda::load_quantized(d, as_t_slice::<BlockQ5_1>(&data)),
214 GgmlDType::Q8_0 => cuda::load_quantized(d, as_t_slice::<BlockQ8_0>(&data)),
215 GgmlDType::Q8_1 => cuda::load_quantized(d, as_t_slice::<BlockQ8_1>(&data)),
216 GgmlDType::Q2K => cuda::load_quantized(d, as_t_slice::<BlockQ2K>(&data)),
217 GgmlDType::Q3K => cuda::load_quantized(d, as_t_slice::<BlockQ3K>(&data)),
218 GgmlDType::Q4K => cuda::load_quantized(d, as_t_slice::<BlockQ4K>(&data)),
219 GgmlDType::Q5K => cuda::load_quantized(d, as_t_slice::<BlockQ5K>(&data)),
220 GgmlDType::Q6K => cuda::load_quantized(d, as_t_slice::<BlockQ6K>(&data)),
221 GgmlDType::Q8K => cuda::load_quantized(d, as_t_slice::<BlockQ8K>(&data)),
222 GgmlDType::IQ4_NL => cuda::load_quantized(d, as_t_slice::<BlockIQ4nl>(&data)),
223 GgmlDType::IQ4_XS => cuda::load_quantized(d, as_t_slice::<BlockIQ4xs>(&data)),
224 GgmlDType::MXFP4 => cuda::load_quantized(d, as_t_slice::<BlockMXFP4>(&data)),
225 GgmlDType::BF16 => cuda::load_quantized(d, as_t_slice::<bf16>(&data)),
226 GgmlDType::I32 => cuda::load_quantized(d, as_t_slice::<i32>(&data)),
227 GgmlDType::IQ2_XXS => cuda::load_quantized(d, as_t_slice::<BlockIQ2xxs>(&data)),
235 GgmlDType::IQ2_XS => cuda::load_quantized(d, as_t_slice::<BlockIQ2xs>(&data)),
236 GgmlDType::IQ2_S => cuda::load_quantized(d, as_t_slice::<BlockIQ2s>(&data)),
237 GgmlDType::IQ3_XXS => cuda::load_quantized(d, as_t_slice::<BlockIQ3xxs>(&data)),
238 GgmlDType::IQ3_S => cuda::load_quantized(d, as_t_slice::<BlockIQ3s>(&data)),
239 GgmlDType::IQ1_S => cuda::load_quantized(d, as_t_slice::<BlockIQ1s>(&data)),
240 GgmlDType::IQ1_M => cuda::load_quantized(d, as_t_slice::<BlockIQ1m>(&data)),
241 GgmlDType::TQ1_0 => cuda::load_quantized(d, as_t_slice::<BlockTQ1_0>(&data)),
242 GgmlDType::TQ2_0 => cuda::load_quantized(d, as_t_slice::<BlockTQ2_0>(&data)),
243 GgmlDType::NVFP4 => cuda::load_quantized(d, as_t_slice::<BlockNVFP4>(&data)),
244 GgmlDType::Q1_0 => cuda::load_quantized(d, as_t_slice::<BlockQ1_0>(&data)),
245 },
246 #[cfg(feature = "rocm")]
247 Device::Rocm(d) => Ok(Self::Rocm(dtype.from_data(data), d.clone())),
248 #[cfg(feature = "vulkan")]
249 Device::Vulkan(d) => Ok(Self::Vulkan(dtype.from_data(data), d.clone())),
250 #[cfg(feature = "wgpu")]
251 Device::Wgpu(d) => Ok(Self::Wgpu(dtype.from_data(data), d.clone())),
252 }
253 }
254
255 fn block_size(&self) -> usize {
256 match self {
257 QStorage::Cpu(storage) => storage.block_size(),
258 QStorage::Metal(storage) => storage.dtype().block_size(),
259 QStorage::Cuda(storage) => storage.dtype().block_size(),
260 #[cfg(feature = "rocm")]
261 QStorage::Rocm(storage, _) => storage.block_size(),
262 #[cfg(feature = "vulkan")]
263 QStorage::Vulkan(storage, _) => storage.block_size(),
264 #[cfg(feature = "wgpu")]
265 QStorage::Wgpu(storage, _) => storage.block_size(),
266 QStorage::Stream(bank) => bank.dtype().block_size(),
267 }
268 }
269
270 fn dtype(&self) -> GgmlDType {
271 match self {
272 QStorage::Cpu(storage) => storage.dtype(),
273 QStorage::Metal(storage) => storage.dtype(),
274 QStorage::Cuda(storage) => storage.dtype(),
275 #[cfg(feature = "rocm")]
276 QStorage::Rocm(storage, _) => storage.dtype(),
277 #[cfg(feature = "vulkan")]
278 QStorage::Vulkan(storage, _) => storage.dtype(),
279 #[cfg(feature = "wgpu")]
280 QStorage::Wgpu(storage, _) => storage.dtype(),
281 QStorage::Stream(bank) => bank.dtype(),
282 }
283 }
284
285 fn device(&self) -> Device {
286 match self {
287 QStorage::Cpu(_storage) => Device::Cpu,
288 QStorage::Metal(storage) => Device::Metal(storage.device().clone()),
289 QStorage::Cuda(storage) => Device::Cuda(storage.device().clone()),
290 #[cfg(feature = "rocm")]
291 QStorage::Rocm(_storage, device) => Device::Rocm(device.clone()),
292 #[cfg(feature = "vulkan")]
293 QStorage::Vulkan(_storage, device) => Device::Vulkan(device.clone()),
294 #[cfg(feature = "wgpu")]
295 QStorage::Wgpu(_storage, device) => Device::Wgpu(device.clone()),
296 QStorage::Stream(_) => Device::Cpu,
297 }
298 }
299
300 fn size_in_bytes(&self) -> usize {
301 match self {
302 QStorage::Cpu(storage) => storage.storage_size_in_bytes(),
303 QStorage::Metal(storage) => storage.storage_size_in_bytes(),
304 QStorage::Cuda(storage) => storage.storage_size_in_bytes(),
305 #[cfg(feature = "rocm")]
306 QStorage::Rocm(storage, _) => storage.storage_size_in_bytes(),
307 #[cfg(feature = "vulkan")]
308 QStorage::Vulkan(storage, _) => storage.storage_size_in_bytes(),
309 #[cfg(feature = "wgpu")]
310 QStorage::Wgpu(storage, _) => storage.storage_size_in_bytes(),
311 QStorage::Stream(bank) => bank.logical_bytes(),
312 }
313 }
314
315 fn quantize(&mut self, src: &Storage) -> Result<()> {
316 match (self, src) {
317 (QStorage::Cpu(storage), Storage::Cpu(src)) => {
318 storage.from_float(src.as_slice::<f32>()?);
319 }
320 (QStorage::Metal(storage), Storage::Metal(src)) => storage.quantize(src)?,
321 (QStorage::Cuda(storage), Storage::Cuda(src)) => storage.quantize(src)?,
322 _ => crate::bail!("Invalid quantize storage locations do not match"),
323 }
324 Ok(())
325 }
326
327 fn quantize_imatrix(
328 &mut self,
329 src: &Storage,
330 imatrix_weights: &[f32],
331 n_per_row: usize,
332 ) -> Result<()> {
333 match (self, src) {
334 (QStorage::Cpu(storage), Storage::Cpu(src)) => {
335 storage.from_float_imatrix(src.as_slice::<f32>()?, imatrix_weights, n_per_row);
336 }
337 (QStorage::Metal(storage), Storage::Metal(src)) => {
338 storage.quantize_imatrix(src, imatrix_weights, n_per_row)?
339 }
340 (QStorage::Cuda(storage), Storage::Cuda(src)) => {
341 storage.quantize_imatrix(src, imatrix_weights, n_per_row)?
342 }
343 _ => crate::bail!("Invalid quantize storage locations do not match"),
344 }
345 Ok(())
346 }
347
348 fn quantize_onto(&mut self, src: &Storage) -> Result<()> {
349 match (self, src) {
350 (QStorage::Cpu(storage), Storage::Cpu(src)) => {
351 storage.from_float(src.as_slice::<f32>()?);
352 }
353 (QStorage::Metal(storage), Storage::Cpu(src)) => storage.quantize_onto(src)?,
354 (QStorage::Cuda(storage), Storage::Cpu(src)) => storage.quantize_onto(src)?,
355 _ => crate::bail!("Invalid quantize source storage locations: not on cpu"),
356 }
357 Ok(())
358 }
359
360 fn quantize_imatrix_onto(
361 &mut self,
362 src: &Storage,
363 imatrix_weights: &[f32],
364 n_per_row: usize,
365 ) -> Result<()> {
366 match (self, src) {
367 (QStorage::Cpu(storage), Storage::Cpu(src)) => {
368 storage.from_float_imatrix(src.as_slice::<f32>()?, imatrix_weights, n_per_row);
369 }
370 (QStorage::Metal(storage), Storage::Cpu(src)) => {
371 storage.quantize_imatrix_onto(src, imatrix_weights, n_per_row)?
372 }
373 (QStorage::Cuda(storage), Storage::Cpu(src)) => {
374 storage.quantize_imatrix_onto(src, imatrix_weights, n_per_row)?
375 }
376 _ => crate::bail!("Invalid quantize storage locations do not match"),
377 }
378 Ok(())
379 }
380
381 fn dequantize(&self, elem_count: usize) -> Result<Storage> {
382 match self {
383 QStorage::Cpu(storage) => Ok(Storage::Cpu(storage.dequantize(elem_count)?)),
384 QStorage::Metal(storage) => Ok(Storage::Metal(storage.dequantize(elem_count)?)),
385 QStorage::Cuda(storage) => Ok(Storage::Cuda(storage.dequantize(elem_count)?)),
386 #[cfg(feature = "rocm")]
387 QStorage::Rocm(storage, device) => {
388 use crate::backend::BackendDevice;
390 let cpu = storage.dequantize(elem_count)?;
391 Ok(Storage::Rocm(device.storage_from_cpu_storage(&cpu)?))
392 }
393 #[cfg(feature = "vulkan")]
394 QStorage::Vulkan(storage, device) => {
395 let cpu = storage.dequantize(elem_count)?;
397 Ok(Storage::Vulkan(device.upload_f32(cpu.as_slice::<f32>()?)?))
398 }
399 #[cfg(feature = "wgpu")]
400 QStorage::Wgpu(storage, device) => {
401 let cpu = storage.dequantize(elem_count)?;
403 Ok(Storage::Wgpu(device.upload_f32(cpu.as_slice::<f32>()?)?))
404 }
405 QStorage::Stream(_) => {
406 crate::bail!("streaming expert bank has no whole-tensor dequantize; consume it via indexed_moe_forward")
407 }
408 }
409 }
410
411 fn data(&self) -> Result<Cow<'_, [u8]>> {
412 match self {
413 QStorage::Cpu(storage) => {
414 let data_ptr = storage.as_ptr();
415 let size_in_bytes = storage.storage_size_in_bytes();
416 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
417 Ok(Cow::from(data))
418 }
419 QStorage::Cuda(storage) => Ok(Cow::from(storage.data()?)),
420 QStorage::Metal(storage) => Ok(Cow::from(storage.data()?)),
421 #[cfg(feature = "rocm")]
422 QStorage::Rocm(storage, _) => {
423 let data_ptr = storage.as_ptr();
424 let size_in_bytes = storage.storage_size_in_bytes();
425 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
426 Ok(Cow::from(data))
427 }
428 #[cfg(feature = "vulkan")]
429 QStorage::Vulkan(storage, _) => {
430 let data_ptr = storage.as_ptr();
431 let size_in_bytes = storage.storage_size_in_bytes();
432 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
433 Ok(Cow::from(data))
434 }
435 #[cfg(feature = "wgpu")]
436 QStorage::Wgpu(storage, _) => {
437 let data_ptr = storage.as_ptr();
438 let size_in_bytes = storage.storage_size_in_bytes();
439 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
440 Ok(Cow::from(data))
441 }
442 QStorage::Stream(_) => {
443 crate::bail!(
444 "streaming expert bank is not resident; consume it via indexed_moe_forward"
445 )
446 }
447 }
448 }
449
450 pub fn device_ptr(&self) -> Result<*const u8> {
451 match self {
452 QStorage::Cuda(storage) => storage.device_ptr(),
453 #[cfg(feature = "rocm")]
454 QStorage::Rocm(..) => crate::bail!("not implemented"),
455 #[cfg(feature = "vulkan")]
456 QStorage::Vulkan(..) => crate::bail!("not implemented"),
457 #[cfg(feature = "wgpu")]
458 QStorage::Wgpu(..) => crate::bail!("not implemented"),
459 QStorage::Metal(_) | QStorage::Cpu(_) | QStorage::Stream(_) => {
460 crate::bail!("not implemented");
461 }
462 }
463 }
464
465 #[cfg(feature = "cuda")]
466 pub fn device_ptr_with_guard<'a>(
467 &'a self,
468 stream: &'a crate::cuda_backend::cudarc::driver::CudaStream,
469 ) -> Result<(
470 *const u8,
471 crate::cuda_backend::cudarc::driver::SyncOnDrop<'a>,
472 )> {
473 match self {
474 QStorage::Cuda(storage) => storage.device_ptr_with_guard(stream),
475 QStorage::Metal(_) | QStorage::Cpu(_) | QStorage::Stream(_) => {
476 crate::bail!("not implemented");
477 }
478 }
479 }
480}
481
482#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
483pub enum GgmlDType {
484 F32,
485 F16,
486 BF16,
487 I32,
490 Q4_0,
491 Q4_1,
492 Q5_0,
493 Q5_1,
494 Q8_0,
495 Q8_1,
496 Q2K,
497 Q3K,
498 Q4K,
499 Q5K,
500 Q6K,
501 Q8K,
502 #[allow(non_camel_case_types)]
503 IQ4_NL,
504 #[allow(non_camel_case_types)]
505 IQ4_XS,
506 MXFP4,
508 #[allow(non_camel_case_types)]
510 IQ2_XXS,
511 #[allow(non_camel_case_types)]
512 IQ2_XS,
513 #[allow(non_camel_case_types)]
514 IQ3_XXS,
515 #[allow(non_camel_case_types)]
516 IQ1_S,
517 #[allow(non_camel_case_types)]
518 IQ3_S,
519 #[allow(non_camel_case_types)]
520 IQ2_S,
521 #[allow(non_camel_case_types)]
522 IQ1_M,
523 TQ1_0,
524 TQ2_0,
525 NVFP4,
526 Q1_0,
527}
528
529use crate::for_each_quant;
541
542macro_rules! gen_from_u32 {
543 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
544 pub(crate) fn from_u32(u: u32) -> Result<Self> {
545 let dtype = match u {
546 0 => Self::F32,
547 1 => Self::F16,
548 30 => Self::BF16,
549 26 => Self::I32,
550 $( $id => Self::$v, )+
551 _ => crate::bail!("unknown dtype for tensor {u}"),
552 };
553 Ok(dtype)
554 }
555 };
556}
557
558macro_rules! gen_to_u32 {
559 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
560 pub fn to_u32(self) -> u32 {
564 match self {
565 Self::F32 => 0,
566 Self::F16 => 1,
567 Self::BF16 => 30,
568 Self::I32 => 26,
569 $( Self::$v => $id, )+
570 }
571 }
572 };
573}
574
575macro_rules! gen_cpu_zeros {
576 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
577 pub fn cpu_zeros(&self, elem_count: usize) -> Box<dyn QuantizedType> {
579 match self {
580 Self::F32 => Box::new(vec![f32::zeros(); elem_count]),
581 Self::F16 => Box::new(vec![f16::zeros(); elem_count]),
582 Self::BF16 => Box::new(vec![bf16::zeros(); elem_count]),
583 Self::I32 => Box::new(vec![0i32; elem_count]),
584 $( Self::$v => Box::new(vec![<$b>::zeros(); elem_count / <$b>::BLCK_SIZE]), )+
585 }
586 }
587 };
588}
589
590macro_rules! gen_from_data {
591 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
592 pub fn from_data(&self, data: Cow<'_, [u8]>) -> Box<dyn QuantizedType> {
593 match self {
594 Self::F32 => Box::new(as_t_slice::<f32>(&data).to_vec()),
595 Self::F16 => Box::new(as_t_slice::<f16>(&data).to_vec()),
596 Self::BF16 => Box::new(as_t_slice::<bf16>(&data).to_vec()),
597 Self::I32 => Box::new(as_t_slice::<i32>(&data).to_vec()),
598 $( Self::$v => Box::new(as_t_slice::<$b>(&data).to_vec()), )+
599 }
600 }
601 };
602}
603
604macro_rules! gen_type_size {
605 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
606 pub fn type_size(&self) -> usize {
608 use k_quants::*;
609 match self {
610 Self::F32 => 4,
611 Self::F16 | Self::BF16 => 2,
612 Self::I32 => 4,
613 $( Self::$v => std::mem::size_of::<$b>(), )+
614 }
615 }
616 };
617}
618
619macro_rules! gen_type_align {
620 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
621 pub fn type_align(&self) -> usize {
626 use k_quants::*;
627 match self {
628 Self::F32 => std::mem::align_of::<f32>(),
629 Self::F16 | Self::BF16 => std::mem::align_of::<f16>(),
630 Self::I32 => std::mem::align_of::<i32>(),
631 $( Self::$v => std::mem::align_of::<$b>(), )+
632 }
633 }
634 };
635}
636
637macro_rules! gen_from_mmap {
638 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
639 #[allow(clippy::wrong_self_convention)] pub(crate) fn from_mmap(
646 &self,
647 mmap: Arc<memmap2::Mmap>,
648 offset: usize,
649 n_blocks: usize,
650 ) -> Box<dyn QuantizedType> {
651 match self {
652 Self::F32 => Box::new(QMmap::<f32>::new(mmap, offset, n_blocks)),
653 Self::F16 => Box::new(QMmap::<f16>::new(mmap, offset, n_blocks)),
654 Self::BF16 => Box::new(QMmap::<bf16>::new(mmap, offset, n_blocks)),
655 Self::I32 => Box::new(QMmap::<i32>::new(mmap, offset, n_blocks)),
656 $( Self::$v => Box::new(QMmap::<$b>::new(mmap, offset, n_blocks)), )+
657 }
658 }
659 };
660}
661
662impl GgmlDType {
663 for_each_quant!(gen_from_u32);
664 for_each_quant!(gen_to_u32);
665 for_each_quant!(gen_cpu_zeros);
666 for_each_quant!(gen_from_data);
667 for_each_quant!(gen_from_mmap);
668 for_each_quant!(gen_type_size);
669 for_each_quant!(gen_type_align);
670
671 pub fn block_size(&self) -> usize {
673 match self {
674 Self::F32 => 1,
675 Self::F16 | Self::BF16 => 1,
676 Self::I32 => 1,
677 Self::Q4_0 => k_quants::QK4_0,
678 Self::Q4_1 => k_quants::QK4_1,
679 Self::Q5_0 => k_quants::QK5_0,
680 Self::Q5_1 => k_quants::QK5_1,
681 Self::Q8_0 => k_quants::QK8_0,
682 Self::Q8_1 => k_quants::QK8_1,
683 Self::IQ4_NL => k_quants::QK4_NL,
684 Self::MXFP4 => k_quants::QK_MXFP4,
685 Self::Q1_0 => iq_quants::QK1_0,
686 Self::NVFP4 => iq_quants::QK_NVFP4,
687 Self::Q2K
688 | Self::Q3K
689 | Self::Q4K
690 | Self::Q5K
691 | Self::Q6K
692 | Self::Q8K
693 | Self::IQ4_XS
694 | Self::IQ2_XXS
695 | Self::IQ2_XS
696 | Self::IQ3_XXS
697 | Self::IQ1_S
698 | Self::IQ3_S
699 | Self::IQ2_S
700 | Self::IQ1_M
701 | Self::TQ1_0
702 | Self::TQ2_0 => k_quants::QK_K,
703 }
704 }
705}
706
707pub trait QuantizedType: Send + Sync {
709 fn dtype(&self) -> GgmlDType;
710 fn matmul_t(&self, mkn: (usize, usize, usize), lhs: &[f32], dst: &mut [f32]) -> Result<()>;
711 fn matmul_t_f16(&self, mkn: (usize, usize, usize), lhs: &[f16], dst: &mut [f16]) -> Result<()>;
712 fn dequantize(&self, elem_count: usize) -> Result<CpuStorage>;
713 fn storage_size_in_bytes(&self) -> usize;
714 fn as_ptr(&self) -> *const u8;
715 fn block_size(&self) -> usize;
716 #[allow(clippy::wrong_self_convention)]
717 fn from_float(&mut self, xs: &[f32]);
718 #[allow(clippy::wrong_self_convention)]
719 fn from_float_imatrix(&mut self, xs: &[f32], imatrix_weights: &[f32], n_per_row: usize);
720 fn size(&self) -> usize;
721}
722
723impl<T: k_quants::GgmlType + Send + Sync> QuantizedType for Vec<T> {
724 fn matmul_t(&self, mkn: (usize, usize, usize), lhs: &[f32], dst: &mut [f32]) -> Result<()> {
725 k_quants::matmul(mkn, lhs, self.as_slice(), dst)
726 }
727 fn matmul_t_f16(&self, mkn: (usize, usize, usize), lhs: &[f16], dst: &mut [f16]) -> Result<()> {
728 k_quants::matmul_f16(mkn, lhs, self.as_slice(), dst)
729 }
730
731 fn size(&self) -> usize {
732 self.len() * core::mem::size_of::<T>()
733 }
734
735 fn from_float(&mut self, xs: &[f32]) {
736 T::from_float(xs, self)
737 }
738
739 fn from_float_imatrix(&mut self, xs: &[f32], imatrix_weights: &[f32], n_per_row: usize) {
740 T::from_float_imatrix(xs, self, imatrix_weights, n_per_row)
741 }
742
743 fn dtype(&self) -> GgmlDType {
744 T::DTYPE
745 }
746
747 fn block_size(&self) -> usize {
748 T::BLCK_SIZE
749 }
750
751 fn dequantize(&self, elem_count: usize) -> Result<CpuStorage> {
752 let mut ys = vec![0.0f32; elem_count];
753 T::to_float(self.as_slice(), &mut ys);
754 Ok(CpuStorage::F32(ys))
755 }
756
757 fn storage_size_in_bytes(&self) -> usize {
758 self.len() * std::mem::size_of::<T>()
759 }
760
761 fn as_ptr(&self) -> *const u8 {
762 self.as_ptr() as *const u8
763 }
764}
765
766pub struct QMmap<T> {
779 mmap: Arc<memmap2::Mmap>,
780 offset: usize,
782 n_blocks: usize,
784 _t: std::marker::PhantomData<T>,
785}
786
787impl<T> QMmap<T> {
788 fn new(mmap: Arc<memmap2::Mmap>, offset: usize, n_blocks: usize) -> Self {
789 Self {
790 mmap,
791 offset,
792 n_blocks,
793 _t: std::marker::PhantomData,
794 }
795 }
796
797 #[inline]
802 fn as_slice(&self) -> &[T] {
803 let len = self.n_blocks * std::mem::size_of::<T>();
804 as_t_slice::<T>(&self.mmap[self.offset..self.offset + len])
805 }
806}
807
808impl<T: k_quants::GgmlType + Send + Sync> QuantizedType for QMmap<T> {
809 fn matmul_t(&self, mkn: (usize, usize, usize), lhs: &[f32], dst: &mut [f32]) -> Result<()> {
810 k_quants::matmul(mkn, lhs, self.as_slice(), dst)
811 }
812
813 fn matmul_t_f16(&self, mkn: (usize, usize, usize), lhs: &[f16], dst: &mut [f16]) -> Result<()> {
814 k_quants::matmul_f16(mkn, lhs, self.as_slice(), dst)
815 }
816
817 fn size(&self) -> usize {
818 self.n_blocks * std::mem::size_of::<T>()
819 }
820
821 fn from_float(&mut self, _xs: &[f32]) {
822 panic!("QMmap is read-only: cannot quantize into a memory-mapped weight region")
823 }
824
825 fn from_float_imatrix(&mut self, _xs: &[f32], _imatrix_weights: &[f32], _n_per_row: usize) {
826 panic!("QMmap is read-only: cannot quantize into a memory-mapped weight region")
827 }
828
829 fn dtype(&self) -> GgmlDType {
830 T::DTYPE
831 }
832
833 fn block_size(&self) -> usize {
834 T::BLCK_SIZE
835 }
836
837 fn dequantize(&self, elem_count: usize) -> Result<CpuStorage> {
838 let mut ys = vec![0.0f32; elem_count];
839 T::to_float(self.as_slice(), &mut ys);
840 Ok(CpuStorage::F32(ys))
841 }
842
843 fn storage_size_in_bytes(&self) -> usize {
844 self.n_blocks * std::mem::size_of::<T>()
845 }
846
847 fn as_ptr(&self) -> *const u8 {
848 self.as_slice().as_ptr() as *const u8
849 }
850}
851
852impl std::fmt::Debug for QTensor {
853 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
854 write!(f, "QTensor[{:?}; {:?}]", self.shape, self.dtype())
855 }
856}
857
858fn check_shape(shape: &Shape, block_size: usize) -> Result<()> {
859 let dims = shape.dims();
860 if dims.is_empty() {
861 crate::bail!("scalar tensor cannot be quantized {shape:?}")
862 }
863 if !dims[dims.len() - 1].is_multiple_of(block_size) {
864 crate::bail!(
865 "quantized tensor must have their last dim divisible by block size {shape:?} {}",
866 block_size
867 )
868 }
869 Ok(())
870}
871
872impl QTensor {
873 fn make(storage: QStorage, shape: Shape) -> Self {
876 Self {
877 storage,
878 shape,
879 banks: ResidentBanks::default(),
880 }
881 }
882
883 pub fn new<S: Into<Shape>>(storage: QStorage, shape: S) -> Result<Self> {
884 let shape = shape.into();
885 check_shape(&shape, storage.block_size())?;
886 Ok(Self::make(storage, shape))
887 }
888
889 pub fn quantize(src: &Tensor, dtype: GgmlDType) -> Result<Self> {
890 let shape = src.shape();
891 let block_size = dtype.block_size();
892 check_shape(shape, block_size)?;
893 let src = src.to_dtype(crate::DType::F32)?.flatten_all()?;
894 let elem_count = shape.elem_count();
895 if !elem_count.is_multiple_of(block_size) {
896 crate::bail!(
897 "tensor size ({shape:?}) is not divisible by block size {}",
898 block_size
899 )
900 }
901 let mut storage = src.device().qzeros(elem_count, dtype)?;
902 storage.quantize(&src.storage())?;
903 Ok(Self::make(storage, shape.clone()))
904 }
905
906 pub fn quantize_imatrix(
907 src: &Tensor,
908 imatrix_weights: &[f32],
909 dtype: GgmlDType,
910 ) -> Result<Self> {
911 let n_per_row = src.dim(D::Minus1)?;
914 if imatrix_weights.len() != n_per_row {
915 crate::bail!(
916 "imatrix weights must have the same length {} as the last dim of src {}",
917 imatrix_weights.len(),
918 src.dim(D::Minus1)?
919 );
920 }
921
922 let shape = src.shape();
923 let block_size = dtype.block_size();
924 check_shape(shape, block_size)?;
925 let src = src.to_dtype(crate::DType::F32)?.flatten_all()?;
926 let elem_count = shape.elem_count();
927 if !elem_count.is_multiple_of(block_size) {
928 crate::bail!(
929 "tensor size ({shape:?}) is not divisible by block size {}",
930 block_size
931 );
932 }
933 let mut storage = src.device().qzeros(elem_count, dtype)?;
934 storage.quantize_imatrix(&src.storage(), imatrix_weights, n_per_row)?;
935 Ok(Self::make(storage, shape.clone()))
936 }
937
938 pub fn quantize_imatrix_onto(
940 src: &Tensor,
941 imatrix_weights: &[f32],
942 dtype: GgmlDType,
943 dev: &Device,
944 ) -> Result<Self> {
945 if !src.device().is_cpu() {
946 crate::bail!(
947 "`quantize_onto` expects a `src` to be on the cpu, got {:?}.",
948 src.device()
949 )
950 }
951 let n_per_row = src.dim(D::Minus1)?;
954 if imatrix_weights.len() != n_per_row {
955 crate::bail!(
956 "imatrix weights must have the same length {} as the last dim of src {}",
957 imatrix_weights.len(),
958 src.dim(D::Minus1)?
959 );
960 }
961 let shape = src.shape();
962 let block_size = dtype.block_size();
963 check_shape(shape, block_size)?;
964 let src = src.to_dtype(crate::DType::F32)?.flatten_all()?;
965 let elem_count = shape.elem_count();
966 if !elem_count.is_multiple_of(block_size) {
967 crate::bail!(
968 "tensor size ({shape:?}) is not divisible by block size {}",
969 block_size
970 )
971 }
972 let mut storage = dev.qzeros(elem_count, dtype)?;
974 storage.quantize_imatrix_onto(&src.storage(), imatrix_weights, n_per_row)?;
975 Ok(Self::make(storage, shape.clone()))
976 }
977
978 pub fn quantize_onto(src: &Tensor, dtype: GgmlDType, dev: &Device) -> Result<Self> {
980 if !src.device().is_cpu() {
981 crate::bail!(
982 "`quantize_onto` expects a `src` to be on the cpu, got {:?}.",
983 src.device()
984 )
985 }
986 let shape = src.shape();
987 let block_size = dtype.block_size();
988 check_shape(shape, block_size)?;
989 let src = src.to_dtype(crate::DType::F32)?.flatten_all()?;
990 let elem_count = shape.elem_count();
991 if !elem_count.is_multiple_of(block_size) {
992 crate::bail!(
993 "tensor size ({shape:?}) is not divisible by block size {}",
994 block_size
995 )
996 }
997 let mut storage = dev.qzeros(elem_count, dtype)?;
999 storage.quantize_onto(&src.storage())?;
1000 Ok(Self::make(storage, shape.clone()))
1001 }
1002
1003 pub fn dtype(&self) -> GgmlDType {
1004 self.storage.dtype()
1005 }
1006
1007 pub fn device(&self) -> Device {
1008 self.storage.device()
1009 }
1010
1011 pub fn rank(&self) -> usize {
1012 self.shape.rank()
1013 }
1014
1015 pub fn shape(&self) -> &Shape {
1016 &self.shape
1017 }
1018
1019 pub fn dequantize(&self, device: &Device) -> Result<Tensor> {
1020 let storage = self.storage.dequantize(self.shape.elem_count())?;
1021 let none = crate::op::BackpropOp::none();
1022 crate::tensor::from_storage(storage, self.shape.clone(), none, false).to_device(device)
1023 }
1024
1025 pub fn dequantize_f16(&self, device: &Device) -> Result<Tensor> {
1026 match &self.storage {
1029 QStorage::Cuda(s) => {
1030 let s = s.dequantize_f16(self.shape.elem_count())?;
1031 let none = crate::op::BackpropOp::none();
1032 crate::tensor::from_storage(Storage::Cuda(s), self.shape.clone(), none, false)
1033 .to_device(device)
1034 }
1035 _ => {
1036 let s = self.dequantize(device)?.to_dtype(crate::DType::F16)?;
1037 Ok(s)
1038 }
1039 }
1040 }
1041
1042 pub fn storage_size_in_bytes(&self) -> usize {
1043 self.storage.size_in_bytes()
1044 }
1045
1046 pub fn data(&self) -> Result<Cow<'_, [u8]>> {
1047 self.storage.data()
1048 }
1049
1050 #[cfg(feature = "rocm")]
1054 fn rocm_moe_bank(&self, dev: &crate::RocmDevice) -> Result<std::sync::Arc<crate::RocmStorage>> {
1055 use crate::backend::BackendDevice;
1056 let bank = self.data()?;
1057 cache_or_upload(&self.banks.rocm, bank.as_ref(), |b| {
1058 dev.storage_from_slice(b)
1059 })
1060 }
1061
1062 #[cfg(feature = "vulkan")]
1066 fn vulkan_moe_bank(
1067 &self,
1068 dev: &crate::VulkanDevice,
1069 e_cnt: usize,
1070 n: usize,
1071 k: usize,
1072 ) -> Result<std::sync::Arc<crate::VulkanStorage>> {
1073 let bank = self.data()?;
1074 let dt = self.storage.dtype();
1075 cache_or_upload(&self.banks.vulkan, bank.as_ref(), |b| match dt {
1076 GgmlDType::Q8_0 => dev.quantize_q8_blocks(b, e_cnt * n, k),
1077 GgmlDType::Q6K => dev.quantize_q6k(b, e_cnt * n, k),
1078 _ => dev.upload_qweight(b),
1079 })
1080 }
1081
1082 #[cfg(feature = "vulkan")]
1086 fn vulkan_moe_bank_split(
1087 &self,
1088 dev: &crate::VulkanDevice,
1089 e_cnt: usize,
1090 n: usize,
1091 k: usize,
1092 ) -> Result<std::sync::Arc<crate::vulkan::MoeBankSplit>> {
1093 let bank = self.data()?;
1094 let dt = self.storage.dtype();
1095 cache_or_upload(&self.banks.vulkan_split, bank.as_ref(), |b| match dt {
1096 GgmlDType::Q4K => dev.quantize_q4k_split(b, e_cnt * n, k),
1097 GgmlDType::Q6K => dev.quantize_q6k_split(b, e_cnt * n, k),
1098 _ => crate::bail!("vulkan_moe_bank_split: unsupported dtype {dt:?}"),
1099 })
1100 }
1101
1102 #[cfg(feature = "wgpu")]
1105 fn wgpu_moe_bank(&self, dev: &crate::WgpuDevice) -> Result<std::sync::Arc<crate::WgpuStorage>> {
1106 let bank = self.data()?;
1107 cache_or_upload(&self.banks.wgpu, bank.as_ref(), |b| dev.upload_qweight(b))
1108 }
1109
1110 pub fn indexed_moe_forward(&self, x: &Tensor, ids: &Tensor) -> Result<Tensor> {
1111 let ids = &ids.contiguous()?;
1114 match &self.storage {
1115 QStorage::Cuda(s) if cuda::QCudaStorage::supports_indexed_moe(s.dtype()) => {
1121 let out_dtype = x.dtype();
1126 let x = x.to_dtype(crate::DType::F32)?.contiguous()?;
1127 let (x_guard, x_l) = x.storage_and_layout();
1130 let (ids_guard, ids_l) = ids.storage_and_layout();
1131 match (&*x_guard, &*ids_guard) {
1132 (Storage::Cuda(x_storage), Storage::Cuda(ids_storage)) => {
1133 let (storage, out_shape) = s.indexed_moe_forward(
1134 self.shape(),
1135 x_storage,
1136 x_l,
1137 ids_storage,
1138 ids_l,
1139 )?;
1140 crate::tensor::from_storage(
1141 Storage::Cuda(storage),
1142 out_shape,
1143 crate::op::BackpropOp::none(),
1144 false,
1145 )
1146 .to_dtype(out_dtype)
1147 }
1148 _ => {
1149 panic!("Non-cuda indexed_moe_forward is not implemented!");
1150 }
1151 }
1152 }
1153 #[cfg(feature = "cuda")]
1159 QStorage::Cuda(s) if cuda::QCudaStorage::supports_iquant_moe(s.dtype()) => {
1160 let out_dtype = x.dtype();
1161 let (_e_cnt, n, k) = self.shape().dims3()?;
1162 let (t, topk) = ids.dims2()?;
1163 let nrows = t * topk;
1164
1165 if t > 1 {
1171 let x_f32 = x.to_dtype(crate::DType::F32)?.contiguous()?;
1172 let ids_u32 = ids.to_dtype(crate::DType::U32)?.contiguous()?;
1173 let (xs, _) = x_f32.storage_and_layout();
1174 let xc = match &*xs {
1175 Storage::Cuda(c) => c,
1176 _ => crate::bail!("cuda i-quant MoE: x not on cuda after contiguous()"),
1177 };
1178 let (ids_s, _) = ids_u32.storage_and_layout();
1179 let idc = match &*ids_s {
1180 Storage::Cuda(c) => c,
1181 _ => crate::bail!("cuda i-quant MoE: ids not on cuda"),
1182 };
1183 if let Some((st, sh)) = s.moe_iquant_qmmq(
1184 self.shape(),
1185 xc.as_cuda_slice::<f32>()?,
1186 x.shape(),
1187 &idc.as_cuda_slice::<u32>()?.slice(0..),
1188 ids.shape(),
1189 )? {
1190 return crate::tensor::from_storage(
1191 Storage::Cuda(st),
1192 sh,
1193 crate::op::BackpropOp::none(),
1194 false,
1195 )
1196 .to_dtype(out_dtype);
1197 }
1198 }
1199
1200 let sdim = x.dim(1)?; let x_exp = if sdim == topk {
1205 x.clone()
1206 } else {
1207 x.broadcast_as((t, topk, k))?
1208 };
1209 let x_flat = x_exp
1210 .reshape((nrows, k))?
1211 .to_dtype(crate::DType::F32)?
1212 .contiguous()?;
1213 let ids_flat = ids
1214 .reshape((nrows,))?
1215 .to_dtype(crate::DType::U32)?
1216 .contiguous()?;
1217 let (xstore, _) = x_flat.storage_and_layout();
1218 let xc = match &*xstore {
1219 Storage::Cuda(c) => c,
1220 _ => crate::bail!("cuda i-quant MoE: x not on cuda after contiguous()"),
1221 };
1222 let (idstore, _) = ids_flat.storage_and_layout();
1223 let idc = match &*idstore {
1224 Storage::Cuda(c) => c,
1225 _ => crate::bail!("cuda i-quant MoE: ids not on cuda"),
1226 };
1227 let y = s.moe_iquant_dp4a(
1228 &xc.as_cuda_slice::<f32>()?.slice(0..),
1229 &idc.as_cuda_slice::<u32>()?.slice(0..),
1230 nrows,
1231 n,
1232 k,
1233 )?;
1234 let out = crate::tensor::from_storage(
1235 Storage::Cuda(y),
1236 (nrows, n),
1237 crate::op::BackpropOp::none(),
1238 false,
1239 );
1240 out.reshape((t, topk, n))?.to_dtype(out_dtype)
1241 }
1242 #[cfg(feature = "vulkan")]
1248 QStorage::Vulkan(_, vk_dev) if vk_moe_kernel(self.storage.dtype()).is_some() => {
1249 let out_dtype = x.dtype();
1250 let (e_cnt, n, k) = self.shape().dims3()?;
1251 let (t, topk) = ids.dims2()?;
1252 let s = x.dim(1)?; let x_exp = if s == topk {
1254 x.clone()
1255 } else {
1256 x.broadcast_as((t, topk, k))?
1257 };
1258 let nrows = t * topk;
1260 let x_flat = x_exp
1261 .reshape((nrows, k))?
1262 .to_dtype(crate::DType::F32)?
1263 .contiguous()?;
1264 let dt = self.storage.dtype();
1271 let ids_u32 = ids
1272 .reshape((nrows,))?
1273 .to_dtype(crate::DType::U32)?
1274 .contiguous()?;
1275 let y = {
1276 let (store, _) = x_flat.storage_and_layout();
1277 let xv = match &*store {
1278 Storage::Vulkan(v) => v,
1279 _ => crate::bail!("vulkan MoE: x not on vulkan after contiguous()"),
1280 };
1281 let (ids_store, _) = ids_u32.storage_and_layout();
1282 let ids_v = match &*ids_store {
1283 Storage::Vulkan(v) => v,
1284 _ => crate::bail!("vulkan MoE: ids not on vulkan after contiguous()"),
1285 };
1286 if t > 1
1295 && dt == GgmlDType::Q4K
1296 && vk_dev.has_int_dot8()
1297 && std::env::var_os("VK_MOE_PREFILL_GEMM_OFF").is_none()
1298 {
1299 let bank = self.vulkan_moe_bank_split(vk_dev, e_cnt, n, k)?;
1300 let (xq, xsq, xsum) = vk_dev.quantize_act_q8(xv, nrows, k)?;
1304 vk_dev.mmq_q4k_id_gpu(
1307 bank.as_ref(),
1308 &xq,
1309 &xsq,
1310 &xsum,
1311 ids_v,
1312 nrows,
1313 e_cnt,
1314 t,
1315 n,
1316 k,
1317 )?
1318 } else {
1319 match vk_moe_blk_dp4a_kernel(dt, n, k).filter(|_| vk_dev.has_int_dot8()) {
1325 Some((blk, with_xsum)) => {
1326 let bank = self.vulkan_moe_bank_split(vk_dev, e_cnt, n, k)?;
1327 vk_dev.moe_matvec_blk_dp4a_gpu(
1328 blk,
1329 with_xsum,
1330 bank.as_ref(),
1331 xv,
1332 ids_v,
1333 nrows,
1334 n,
1335 k,
1336 )?
1337 }
1338 None => match vk_moe_blk_kernel(dt, n, k) {
1339 Some(blk) => {
1340 let bank = self.vulkan_moe_bank_split(vk_dev, e_cnt, n, k)?;
1341 vk_dev.moe_matvec_blk_gpu(
1342 blk,
1343 bank.as_ref(),
1344 xv,
1345 ids_v,
1346 nrows,
1347 n,
1348 k,
1349 )?
1350 }
1351 None => {
1352 let kernel = vk_moe_kernel(dt).unwrap();
1354 let wbank = self.vulkan_moe_bank(vk_dev, e_cnt, n, k)?;
1355 vk_dev.moe_matvec_gpu(
1356 kernel,
1357 wbank.as_ref(),
1358 xv,
1359 ids_v,
1360 nrows,
1361 n,
1362 k,
1363 )?
1364 }
1365 },
1366 }
1367 }
1368 };
1369 let out = crate::tensor::from_storage(
1370 Storage::Vulkan(y),
1371 (nrows, n),
1372 crate::op::BackpropOp::none(),
1373 false,
1374 );
1375 out.reshape((t, topk, n))?.to_dtype(out_dtype)
1376 }
1377 #[cfg(feature = "wgpu")]
1381 QStorage::Wgpu(_, wgpu_dev) if wgpu_moe_kernel(self.storage.dtype()).is_some() => {
1382 let out_dtype = x.dtype();
1383 let (e_cnt, n, k) = self.shape().dims3()?;
1384 let (t, topk) = ids.dims2()?;
1385 let s = x.dim(1)?; let x_exp = if s == topk {
1387 x.clone()
1388 } else {
1389 x.broadcast_as((t, topk, k))?
1390 };
1391 let nrows = t * topk;
1392 let x_flat = x_exp
1393 .reshape((nrows, k))?
1394 .to_dtype(crate::DType::F32)?
1395 .contiguous()?;
1396 let ids_vec = ids
1397 .reshape((nrows,))?
1398 .to_dtype(crate::DType::U32)?
1399 .to_vec1::<u32>()?;
1400 if let Some(&bad) = ids_vec.iter().find(|&&e| e as usize >= e_cnt) {
1401 crate::bail!("indexed_moe_forward: expert id {bad} >= num_experts {e_cnt}");
1402 }
1403 let kernel = wgpu_moe_kernel(self.storage.dtype()).unwrap();
1405 let wbank = self.wgpu_moe_bank(wgpu_dev)?;
1407 let ids_buf = wgpu_dev.upload_ids(&ids_vec)?;
1408 let y = {
1409 let (store, _) = x_flat.storage_and_layout();
1410 let xv = match &*store {
1411 Storage::Wgpu(v) => v,
1412 _ => crate::bail!("wgpu MoE: x not on wgpu after contiguous()"),
1413 };
1414 wgpu_dev.moe_matvec_gpu(kernel, wbank.as_ref(), xv, &ids_buf, nrows, n, k)?
1415 };
1416 let out = crate::tensor::from_storage(
1417 Storage::Wgpu(y),
1418 (nrows, n),
1419 crate::op::BackpropOp::none(),
1420 false,
1421 );
1422 out.reshape((t, topk, n))?.to_dtype(out_dtype)
1423 }
1424 #[cfg(feature = "rocm")]
1429 QStorage::Rocm(_, rocm_dev)
1430 if crate::RocmQuantType::from_ggml(self.storage.dtype()).is_some() =>
1431 {
1432 let qt = crate::RocmQuantType::from_ggml(self.storage.dtype()).unwrap();
1433 let out_dtype = x.dtype();
1434 let (_e_cnt, n, k) = self.shape().dims3()?;
1437 let (t, topk) = ids.dims2()?;
1438 let s = x.dim(1)?; let x_exp = if s == topk {
1440 x.clone()
1441 } else {
1442 x.broadcast_as((t, topk, k))?
1443 };
1444 let nrows = t * topk;
1445 let use_qmmq = t > 1 && qt.qmmq_capable();
1451 let x_flat = match x_exp.dtype() {
1452 DType::F16 | DType::F32 if use_qmmq => {
1456 x_exp.reshape((nrows, k))?.contiguous()?
1457 }
1458 _ if use_qmmq => x_exp
1459 .reshape((nrows, k))?
1460 .to_dtype(DType::F16)?
1461 .contiguous()?,
1462 DType::BF16 | DType::F16 => x_exp.reshape((nrows, k))?.contiguous()?,
1463 DType::F32 if qt.dp4a_active() => x_exp.reshape((nrows, k))?.contiguous()?,
1467 _ => x_exp
1468 .reshape((nrows, k))?
1469 .to_dtype(DType::F16)?
1470 .contiguous()?,
1471 };
1472 let wbank = self.rocm_moe_bank(rocm_dev)?;
1473 let ids_u32 = ids
1480 .reshape((nrows,))?
1481 .to_dtype(crate::DType::U32)?
1482 .contiguous()?;
1483 let (store, _) = x_flat.storage_and_layout();
1484 let xr = match &*store {
1485 crate::Storage::Rocm(r) => r,
1486 _ => crate::bail!("rocm MoE: x not on rocm after contiguous()"),
1487 };
1488 let (idstore, _) = ids_u32.storage_and_layout();
1489 let idr = match &*idstore {
1490 crate::Storage::Rocm(r) => r,
1491 _ => crate::bail!("rocm MoE: ids not on rocm"),
1492 };
1493 let y = if use_qmmq {
1494 rocm_dev.moe_qmmq_quant(qt, wbank.as_ref(), xr, idr, nrows, n, k)?
1495 } else {
1496 rocm_dev.moe_matvec_quant(qt, wbank.as_ref(), xr, idr, nrows, n, k)?
1497 };
1498 let out = crate::tensor::from_storage(
1499 crate::Storage::Rocm(y),
1500 (nrows, n),
1501 crate::op::BackpropOp::none(),
1502 false,
1503 );
1504 out.reshape((t, topk, n))?.to_dtype(out_dtype)
1505 }
1506 #[cfg(feature = "metal")]
1514 QStorage::Metal(s)
1515 if matches!(&*x.storage(), Storage::Metal(_))
1516 && matches!(&*ids.storage(), Storage::Metal(_)) =>
1517 {
1518 let out_dtype = x.dtype();
1519 let x = x.contiguous()?;
1520 let (xs_guard, x_l) = x.storage_and_layout();
1521 let (ids_guard, ids_l) = ids.storage_and_layout();
1522 let (Storage::Metal(x_storage), Storage::Metal(ids_storage)) =
1523 (&*xs_guard, &*ids_guard)
1524 else {
1525 unreachable!("metal MoE arm is guarded on Metal x/ids storage");
1526 };
1527 let (storage, out_shape) =
1528 s.indexed_moe_forward(self.shape(), x_storage, x_l, ids_storage, ids_l)?;
1529 let out = crate::tensor::from_storage(
1530 Storage::Metal(storage),
1531 out_shape,
1532 crate::op::BackpropOp::none(),
1533 false,
1534 );
1535 out.to_dtype(out_dtype)
1536 }
1537 QStorage::Stream(bank) => {
1541 let (e_cnt, n, k) = self.shape().dims3()?;
1542 let dtype = bank.dtype();
1543 moe_grouped_per_expert(x, ids, n, k, |eid, device| {
1544 if eid as usize >= e_cnt {
1545 crate::bail!("indexed_moe_forward: expert id {eid} >= num_experts {e_cnt}");
1546 }
1547 let bytes = bank.fetch(eid)?;
1548 QStorage::from_data(std::borrow::Cow::Borrowed(&bytes), device, dtype)
1549 })
1550 }
1551 _ => {
1552 let (e_cnt, n, k) = self.shape().dims3()?;
1558 let dtype = self.storage.dtype();
1559 let all_bytes = self.data()?;
1560 let expert_bytes = all_bytes.len() / e_cnt;
1561 moe_grouped_per_expert(x, ids, n, k, |eid, device| {
1562 let off = eid as usize * expert_bytes;
1563 QStorage::from_data(
1564 std::borrow::Cow::Borrowed(&all_bytes[off..off + expert_bytes]),
1565 device,
1566 dtype,
1567 )
1568 })
1569 }
1570 }
1571 }
1572
1573 pub fn device_ptr(&self) -> Result<*const u8> {
1574 match &self.storage {
1575 QStorage::Cuda(storage) => storage.device_ptr(),
1576 #[cfg(feature = "rocm")]
1577 QStorage::Rocm(..) => crate::bail!("not implemented"),
1578 #[cfg(feature = "vulkan")]
1579 QStorage::Vulkan(..) => crate::bail!("not implemented"),
1580 #[cfg(feature = "wgpu")]
1581 QStorage::Wgpu(..) => crate::bail!("not implemented"),
1582 QStorage::Metal(_) | QStorage::Cpu(_) | QStorage::Stream(_) => {
1583 crate::bail!("not implemented");
1584 }
1585 }
1586 }
1587
1588 #[cfg(feature = "cuda")]
1589 pub fn device_ptr_with_guard<'a>(
1590 &'a self,
1591 stream: &'a crate::cuda_backend::cudarc::driver::CudaStream,
1592 ) -> Result<(
1593 *const u8,
1594 crate::cuda_backend::cudarc::driver::SyncOnDrop<'a>,
1595 )> {
1596 self.storage.device_ptr_with_guard(stream)
1597 }
1598}
1599
1600#[derive(Clone, Debug)]
1601pub enum QMatMul {
1602 QTensor(std::sync::Arc<QTensor>),
1603 Tensor(Tensor),
1604 TensorF16(Tensor),
1605 #[cfg(feature = "vulkan")]
1611 VulkanQuant {
1612 qtensor: std::sync::Arc<QTensor>,
1613 wq: std::sync::Arc<crate::VulkanStorage>,
1614 dtype: GgmlDType,
1615 n: usize,
1616 k: usize,
1617 },
1618 #[cfg(feature = "wgpu")]
1622 WgpuQuant {
1623 qtensor: std::sync::Arc<QTensor>,
1624 wq: std::sync::Arc<crate::WgpuStorage>,
1625 dtype: GgmlDType,
1626 n: usize,
1627 k: usize,
1628 },
1629 #[cfg(feature = "rocm")]
1635 RocmQuant {
1636 qtensor: std::sync::Arc<QTensor>,
1637 wq: std::sync::Arc<crate::RocmStorage>,
1638 dtype: GgmlDType,
1639 n: usize,
1640 k: usize,
1641 },
1642}
1643
1644#[cfg(any(feature = "rocm", feature = "vulkan", feature = "wgpu"))]
1654fn cache_or_upload<S>(
1655 slot: &std::sync::OnceLock<std::sync::Arc<S>>,
1656 bank: &[u8],
1657 upload: impl FnOnce(&[u8]) -> Result<S>,
1658) -> Result<std::sync::Arc<S>> {
1659 if let Some(w) = slot.get() {
1660 return Ok(w.clone());
1661 }
1662 let w = std::sync::Arc::new(upload(bank)?);
1663 Ok(slot.get_or_init(|| w).clone())
1666}
1667
1668#[cfg(feature = "vulkan")]
1674fn vk_moe_kernel(dt: GgmlDType) -> Option<&'static str> {
1675 match dt {
1676 GgmlDType::Q4_0 => Some("moe_matvec_q4_0"),
1677 GgmlDType::Q8_0 => Some("moe_matvec_q8_0"),
1678 GgmlDType::Q4K => Some("moe_matvec_q4k"),
1679 GgmlDType::Q6K => Some("moe_matvec_q6k"),
1680 _ => None,
1681 }
1682}
1683
1684#[cfg(feature = "vulkan")]
1690fn vk_moe_blk_kernel(dt: GgmlDType, n: usize, k: usize) -> Option<&'static str> {
1691 if std::env::var_os("VK_MOE_PACKED").is_some() {
1694 return None;
1695 }
1696 match (dt, n, k) {
1697 (GgmlDType::Q4K, 768, 2048) => Some("moe_matvec_q4k_blk_gu"),
1698 (GgmlDType::Q4K, 2048, 768) => Some("moe_matvec_q4k_blk_dn"),
1699 (GgmlDType::Q6K, 2048, 768) => Some("moe_matvec_q6k_blk_dn"),
1700 _ => None,
1701 }
1702}
1703
1704#[cfg(feature = "vulkan")]
1713fn vk_moe_blk_dp4a_kernel(dt: GgmlDType, n: usize, k: usize) -> Option<(&'static str, bool)> {
1714 if std::env::var_os("VK_MOE_PACKED").is_some() || std::env::var_os("VK_MOE_DP4A_OFF").is_some()
1715 {
1716 return None;
1717 }
1718 match (dt, n, k) {
1719 (GgmlDType::Q4K, 768, 2048) => Some(("moe_matvec_q4k_dp4a_blk_gu", true)),
1720 (GgmlDType::Q4K, 2048, 768) => Some(("moe_matvec_q4k_dp4a_blk_dn", true)),
1721 (GgmlDType::Q6K, 2048, 768) => Some(("moe_matvec_q6k_dp4a_blk_dn", false)),
1722 _ => None,
1723 }
1724}
1725
1726#[cfg(feature = "wgpu")]
1728fn wgpu_moe_kernel(dt: GgmlDType) -> Option<&'static str> {
1729 match dt {
1730 GgmlDType::Q4_0 => Some("moe_matvec_q4_0"),
1731 GgmlDType::Q8_0 => Some("moe_matvec_q8_0"),
1732 GgmlDType::Q4K => Some("moe_matvec_q4k"),
1733 _ => None,
1734 }
1735}
1736
1737fn moe_grouped_per_expert(
1742 x: &Tensor,
1743 ids: &Tensor,
1744 n: usize,
1745 k: usize,
1746 mut make_storage: impl FnMut(u32, &Device) -> Result<QStorage>,
1747) -> Result<Tensor> {
1748 use crate::Module; use std::collections::HashMap;
1750 use std::sync::Arc;
1751 let device = x.device();
1752 let out_dtype = x.dtype();
1753 let (t, topk) = ids.dims2()?;
1754 let s = x.dim(1)?; let x_exp = if s == topk {
1756 x.clone()
1757 } else {
1758 x.broadcast_as((t, topk, k))?
1759 };
1760 let x_flat = x_exp
1761 .reshape((t * topk, k))?
1762 .to_dtype(DType::F32)?
1763 .contiguous()?;
1764 let ids_flat = ids.reshape((t * topk,))?.to_dtype(DType::U32)?;
1765 let ids_vec = ids_flat.to_vec1::<u32>()?;
1766 let mut groups: HashMap<u32, Vec<u32>> = HashMap::new();
1767 for (slot, eid) in ids_vec.iter().enumerate() {
1768 groups.entry(*eid).or_default().push(slot as u32);
1769 }
1770 let mut out_flat = Tensor::zeros((t * topk, n), DType::F32, device)?;
1771 for (eid, slots) in groups.into_iter() {
1772 let qs = make_storage(eid, device)?;
1773 let shape: crate::Shape = (n, k).into();
1774 let w_e = QTensor::make(qs, shape);
1775 let qm = QMatMul::from_arc(Arc::new(w_e))?;
1776 let m = slots.len();
1777 let idx = Tensor::from_vec(slots, (m,), device)?;
1778 let x_e = x_flat.index_select(&idx, 0)?; let y_e = qm.forward(&x_e)?.to_dtype(DType::F32)?; out_flat = out_flat.index_add(&idx, &y_e, 0)?;
1781 }
1782 out_flat.reshape((t, topk, n))?.to_dtype(out_dtype)
1783}
1784
1785#[cfg_attr(not(feature = "rocm"), allow(unused_variables))]
1791pub fn moe_combine(ys: &Tensor, scores: &Tensor) -> Result<Tensor> {
1792 let (t, topk, n) = ys.dims3()?;
1793 #[cfg(feature = "rocm")]
1794 if let Device::Rocm(dev) = ys.device() {
1795 let ys_c = ys.contiguous()?;
1796 let scores_c = scores.to_dtype(DType::F32)?.contiguous()?;
1797 let (ys_store, _) = ys_c.storage_and_layout();
1798 let yr = match &*ys_store {
1799 Storage::Rocm(r) => r,
1800 _ => crate::bail!("moe_combine: ys not on rocm after contiguous()"),
1801 };
1802 let (sc_store, _) = scores_c.storage_and_layout();
1803 let sr = match &*sc_store {
1804 Storage::Rocm(r) => r,
1805 _ => crate::bail!("moe_combine: scores not on rocm after contiguous()"),
1806 };
1807 let out = dev.moe_combine(yr, sr, t, topk, n)?;
1808 return Ok(crate::tensor::from_storage(
1809 Storage::Rocm(out),
1810 (t, n),
1811 crate::op::BackpropOp::none(),
1812 false,
1813 ));
1814 }
1815 let out_dtype = ys.dtype();
1820 ys.to_dtype(DType::F32)?
1821 .broadcast_mul(&scores.to_dtype(DType::F32)?.unsqueeze(D::Minus1)?)?
1822 .sum(D::Minus2)?
1823 .to_dtype(out_dtype)
1824}
1825
1826#[cfg_attr(not(feature = "rocm"), allow(unused_variables))]
1831pub fn moe_route(logits: &Tensor, topk: usize, norm: bool) -> Result<(Tensor, Tensor)> {
1832 let (ntok, n_experts) = logits.dims2()?;
1833 #[cfg(feature = "rocm")]
1834 if let Device::Rocm(dev) = logits.device() {
1835 let logits_c = logits.to_dtype(DType::F32)?.contiguous()?;
1836 let (lg_store, _) = logits_c.storage_and_layout();
1837 let lr = match &*lg_store {
1838 Storage::Rocm(r) => r,
1839 _ => crate::bail!("moe_route: logits not on rocm after contiguous()"),
1840 };
1841 let (ids, w) = dev.moe_route(lr, ntok, n_experts, topk, norm)?;
1842 let ids_t = crate::tensor::from_storage(
1843 Storage::Rocm(ids),
1844 (ntok, topk),
1845 crate::op::BackpropOp::none(),
1846 false,
1847 );
1848 let w_t = crate::tensor::from_storage(
1849 Storage::Rocm(w),
1850 (ntok, topk),
1851 crate::op::BackpropOp::none(),
1852 false,
1853 );
1854 return Ok((ids_t, w_t));
1855 }
1856 #[cfg(feature = "cuda")]
1857 if let Device::Cuda(cdev) = logits.device() {
1858 if n_experts <= 256 && topk <= 32 {
1859 let logits_c = logits.to_dtype(DType::F32)?.contiguous()?;
1860 let (lg_store, _) = logits_c.storage_and_layout();
1861 let lr = match &*lg_store {
1862 Storage::Cuda(c) => c,
1863 _ => crate::bail!("moe_route: logits not on cuda after contiguous()"),
1864 };
1865 let lview = lr.as_cuda_slice::<f32>()?.slice(0..);
1866 let (ids, w) = cuda::moe_route(&lview, ntok, n_experts, topk, norm, cdev)?;
1867 let ids_t = crate::tensor::from_storage(
1868 Storage::Cuda(ids),
1869 (ntok, topk),
1870 crate::op::BackpropOp::none(),
1871 false,
1872 );
1873 let w_t = crate::tensor::from_storage(
1874 Storage::Cuda(w),
1875 (ntok, topk),
1876 crate::op::BackpropOp::none(),
1877 false,
1878 );
1879 return Ok((ids_t, w_t));
1880 }
1881 }
1882 #[cfg(feature = "vulkan")]
1886 if let Device::Vulkan(vdev) = logits.device() {
1887 if norm && n_experts == 128 && topk == 8 {
1888 let logits_c = logits.to_dtype(DType::F32)?.contiguous()?;
1889 let (lg_store, _) = logits_c.storage_and_layout();
1890 let lv = match &*lg_store {
1891 Storage::Vulkan(v) => v,
1892 _ => crate::bail!("moe_route: logits not on vulkan after contiguous()"),
1893 };
1894 let (ids, w) = vdev.moe_route_vk(lv, ntok, n_experts, topk)?;
1895 let ids_t = crate::tensor::from_storage(
1896 Storage::Vulkan(ids),
1897 (ntok, topk),
1898 crate::op::BackpropOp::none(),
1899 false,
1900 );
1901 let w_t = crate::tensor::from_storage(
1902 Storage::Vulkan(w),
1903 (ntok, topk),
1904 crate::op::BackpropOp::none(),
1905 false,
1906 );
1907 return Ok((ids_t, w_t));
1908 }
1909 }
1910 let lf = logits.to_dtype(DType::F32)?;
1911 let mx = lf.max_keepdim(D::Minus1)?;
1912 let e = lf.broadcast_sub(&mx)?.exp()?;
1913 let z = e.sum_keepdim(D::Minus1)?;
1914 let p = e.broadcast_div(&z)?;
1915 let (sv, si) = p.sort_last_dim(false)?;
1916 let ids = si.narrow(D::Minus1, 0, topk)?.contiguous()?;
1917 let mut w = sv.narrow(D::Minus1, 0, topk)?.contiguous()?;
1918 if norm {
1919 w = w.broadcast_div(&w.sum_keepdim(D::Minus1)?)?;
1920 }
1921 Ok((ids, w))
1922}
1923
1924pub fn moe_gate_up(
1932 x: &Tensor,
1933 ids: &Tensor,
1934 gate: &QMatMul,
1935 up: &QMatMul,
1936) -> Result<(Tensor, Tensor)> {
1937 #[cfg(feature = "rocm")]
1938 {
1939 if let (QMatMul::QTensor(gq), QMatMul::QTensor(uq)) = (gate, up) {
1940 if let (QStorage::Rocm(_, dev), QStorage::Rocm(..)) = (&gq.storage, &uq.storage) {
1941 let dt = gq.storage.dtype();
1942 if dt == uq.storage.dtype() {
1943 if let Some(qt) = crate::RocmQuantType::from_ggml(dt) {
1944 let (_e, n, k) = gq.shape().dims3()?;
1945 let (t, topk) = ids.dims2()?;
1946 let use_qmmq = t > 1 && qt.qmmq_capable();
1947 if x.dim(1)? == 1 && !use_qmmq {
1948 let nrows = t * topk;
1949 let x_exp = x.broadcast_as((t, topk, k))?;
1950 let x_flat = match x_exp.dtype() {
1951 DType::BF16 | DType::F16 => {
1952 x_exp.reshape((nrows, k))?.contiguous()?
1953 }
1954 DType::F32 if qt.dp4a_active() => {
1955 x_exp.reshape((nrows, k))?.contiguous()?
1956 }
1957 _ => x_exp
1958 .reshape((nrows, k))?
1959 .to_dtype(DType::F16)?
1960 .contiguous()?,
1961 };
1962 let out_dtype = x.dtype();
1963 let ids_u32 =
1964 ids.reshape((nrows,))?.to_dtype(DType::U32)?.contiguous()?;
1965 let gwb = gq.rocm_moe_bank(dev)?;
1966 let uwb = uq.rocm_moe_bank(dev)?;
1967 let (xstore, _) = x_flat.storage_and_layout();
1968 let xr = match &*xstore {
1969 Storage::Rocm(r) => r,
1970 _ => crate::bail!("moe_gate_up: x not on rocm after contiguous()"),
1971 };
1972 let (idstore, _) = ids_u32.storage_and_layout();
1973 let idr = match &*idstore {
1974 Storage::Rocm(r) => r,
1975 _ => crate::bail!("moe_gate_up: ids not on rocm"),
1976 };
1977 let (gy, uy) = dev.moe_matvec_pair(
1978 qt,
1979 gwb.as_ref(),
1980 uwb.as_ref(),
1981 xr,
1982 idr,
1983 nrows,
1984 n,
1985 k,
1986 )?;
1987 let g = crate::tensor::from_storage(
1988 Storage::Rocm(gy),
1989 (nrows, n),
1990 crate::op::BackpropOp::none(),
1991 false,
1992 )
1993 .reshape((t, topk, n))?
1994 .to_dtype(out_dtype)?;
1995 let u = crate::tensor::from_storage(
1996 Storage::Rocm(uy),
1997 (nrows, n),
1998 crate::op::BackpropOp::none(),
1999 false,
2000 )
2001 .reshape((t, topk, n))?
2002 .to_dtype(out_dtype)?;
2003 return Ok((g, u));
2004 }
2005 }
2006 }
2007 }
2008 }
2009 }
2010 #[cfg(feature = "vulkan")]
2017 if std::env::var_os("VK_MOE_GU_FUSE_OFF").is_none() {
2018 if let (QMatMul::QTensor(gq), QMatMul::QTensor(uq)) = (gate, up) {
2019 if let (QStorage::Vulkan(_, dev), QStorage::Vulkan(..)) = (&gq.storage, &uq.storage) {
2020 let dt = gq.storage.dtype();
2021 if dt == uq.storage.dtype() && dev.has_int_dot8() {
2022 let (e_cnt, n, k) = gq.shape().dims3()?;
2023 if uq.shape().dims3()? == (e_cnt, n, k) && x.dim(1)? == 1 {
2025 if let Some((blk, with_xsum)) = vk_moe_blk_dp4a_kernel(dt, n, k) {
2026 let (t, topk) = ids.dims2()?;
2027 let nrows = t * topk;
2028 let x_flat = x
2029 .broadcast_as((t, topk, k))?
2030 .reshape((nrows, k))?
2031 .to_dtype(DType::F32)?
2032 .contiguous()?;
2033 let ids_u32 =
2034 ids.reshape((nrows,))?.to_dtype(DType::U32)?.contiguous()?;
2035 let (xstore, _) = x_flat.storage_and_layout();
2036 let xv = match &*xstore {
2037 Storage::Vulkan(v) => v,
2038 _ => {
2039 crate::bail!("moe_gate_up: x not on vulkan after contiguous()")
2040 }
2041 };
2042 let (idstore, _) = ids_u32.storage_and_layout();
2043 let idv = match &*idstore {
2044 Storage::Vulkan(v) => v,
2045 _ => crate::bail!("moe_gate_up: ids not on vulkan"),
2046 };
2047 let (xq, xs, xsum) = dev.quantize_act_q8(xv, nrows, k)?;
2049 let gbank = gq.vulkan_moe_bank_split(dev, e_cnt, n, k)?;
2050 let ubank = uq.vulkan_moe_bank_split(dev, e_cnt, n, k)?;
2051 let out_dtype = x.dtype();
2052 let (gy, uy) = if t > 1
2056 && dt == GgmlDType::Q4K
2057 && std::env::var_os("VK_MOE_PREFILL_GEMM_OFF").is_none()
2058 {
2059 let (counts, rows) =
2060 dev.moe_expert_rows_vk(idv, nrows, e_cnt, t)?;
2061 let gy = dev.mmq_q4k_id_pre_gpu(
2062 gbank.as_ref(),
2063 &xq,
2064 &xs,
2065 &xsum,
2066 &rows,
2067 &counts,
2068 nrows,
2069 e_cnt,
2070 t,
2071 n,
2072 k,
2073 )?;
2074 let uy = dev.mmq_q4k_id_pre_gpu(
2075 ubank.as_ref(),
2076 &xq,
2077 &xs,
2078 &xsum,
2079 &rows,
2080 &counts,
2081 nrows,
2082 e_cnt,
2083 t,
2084 n,
2085 k,
2086 )?;
2087 (gy, uy)
2088 } else {
2089 let gy = dev.moe_matvec_blk_dp4a_pre_gpu(
2090 blk,
2091 with_xsum,
2092 gbank.as_ref(),
2093 &xq,
2094 &xs,
2095 &xsum,
2096 idv,
2097 nrows,
2098 n,
2099 )?;
2100 let uy = dev.moe_matvec_blk_dp4a_pre_gpu(
2101 blk,
2102 with_xsum,
2103 ubank.as_ref(),
2104 &xq,
2105 &xs,
2106 &xsum,
2107 idv,
2108 nrows,
2109 n,
2110 )?;
2111 (gy, uy)
2112 };
2113 let shape = |o| -> Result<Tensor> {
2114 crate::tensor::from_storage(
2115 Storage::Vulkan(o),
2116 (nrows, n),
2117 crate::op::BackpropOp::none(),
2118 false,
2119 )
2120 .reshape((t, topk, n))?
2121 .to_dtype(out_dtype)
2122 };
2123 return Ok((shape(gy)?, shape(uy)?));
2124 }
2125 }
2126 }
2127 }
2128 }
2129 }
2130 Ok((
2131 gate.indexed_moe_forward(x, ids)?,
2132 up.indexed_moe_forward(x, ids)?,
2133 ))
2134}
2135
2136impl QMatMul {
2137 pub fn from_arc(qtensor: std::sync::Arc<QTensor>) -> Result<Self> {
2138 #[cfg(feature = "vulkan")]
2144 {
2145 let dt = qtensor.dtype();
2146 let native_vk = matches!(
2147 dt,
2148 GgmlDType::Q4_0
2149 | GgmlDType::Q8_0
2150 | GgmlDType::Q4K
2151 | GgmlDType::Q5K
2152 | GgmlDType::Q6K
2153 | GgmlDType::Q2K
2154 | GgmlDType::Q3K
2155 | GgmlDType::IQ4_XS
2156 | GgmlDType::IQ4_NL
2157 | GgmlDType::TQ2_0
2158 | GgmlDType::IQ2_XXS
2159 | GgmlDType::IQ2_S
2160 | GgmlDType::IQ3_XXS
2161 | GgmlDType::IQ3_S
2162 | GgmlDType::IQ1_S
2163 | GgmlDType::IQ1_M
2164 | GgmlDType::IQ2_XS
2165 );
2166 if native_vk {
2167 if let Device::Vulkan(d) = qtensor.device() {
2168 if let Ok((n, k)) = qtensor.shape().dims2() {
2169 let blk = dt.block_size();
2170 if k % blk == 0 {
2171 let bytes = qtensor.data()?;
2172 let wq = match dt {
2179 GgmlDType::Q6K => d.quantize_q6k(&bytes, n, k)?,
2180 GgmlDType::Q3K => d.quantize_q3k(&bytes, n, k)?,
2181 GgmlDType::Q8_0 => d.quantize_q8_blocks(&bytes, n, k)?,
2182 GgmlDType::IQ2_XXS => d.quantize_iq2xxs(&bytes, n, k)?,
2183 GgmlDType::IQ2_XS => d.quantize_iq2xs(&bytes, n, k)?,
2184 GgmlDType::IQ1_M => d.quantize_iq1m(&bytes, n, k)?,
2185 GgmlDType::IQ1_S => d.quantize_iq1s(&bytes, n, k)?,
2186 GgmlDType::IQ3_S => d.quantize_iq3s(&bytes, n, k)?,
2187 GgmlDType::IQ3_XXS => d.quantize_iq3xxs(&bytes, n, k)?,
2188 GgmlDType::IQ2_S => d.quantize_iq2s(&bytes, n, k)?,
2189 _ => d.upload_qweight(&bytes)?,
2190 };
2191 return Ok(Self::VulkanQuant {
2192 qtensor,
2193 wq: std::sync::Arc::new(wq),
2194 dtype: dt,
2195 n,
2196 k,
2197 });
2198 }
2199 }
2200 }
2201 }
2202 }
2203 #[cfg(feature = "wgpu")]
2206 {
2207 let dt = qtensor.dtype();
2208 let native_wgpu = matches!(dt, GgmlDType::Q4_0 | GgmlDType::Q8_0 | GgmlDType::Q4K);
2209 if native_wgpu {
2210 if let Device::Wgpu(d) = qtensor.device() {
2211 if let Ok((n, k)) = qtensor.shape().dims2() {
2212 let blk = dt.block_size();
2213 if k % blk == 0 {
2214 let bytes = qtensor.data()?;
2215 let wq = d.upload_qweight(&bytes)?;
2216 return Ok(Self::WgpuQuant {
2217 qtensor,
2218 wq: std::sync::Arc::new(wq),
2219 dtype: dt,
2220 n,
2221 k,
2222 });
2223 }
2224 }
2225 }
2226 }
2227 }
2228 #[cfg(feature = "rocm")]
2234 {
2235 let dt = qtensor.dtype();
2236 if let Some(qt) = crate::RocmQuantType::from_ggml(dt) {
2241 if let Device::Rocm(d) = qtensor.device() {
2242 if let Ok((n, k)) = qtensor.shape().dims2() {
2243 let blk_ok = k % qt.block_elems() == 0;
2244 if blk_ok {
2245 use crate::backend::BackendDevice;
2246 let bytes = qtensor.data()?;
2247 let wq = d.storage_from_slice(bytes.as_ref())?;
2248 return Ok(Self::RocmQuant {
2249 qtensor,
2250 wq: std::sync::Arc::new(wq),
2251 dtype: dt,
2252 n,
2253 k,
2254 });
2255 }
2256 }
2257 }
2258 }
2259 }
2260 #[cfg(feature = "rocm")]
2266 {
2267 if qtensor.device().is_rocm()
2268 && qtensor.shape().dims().len() == 3
2269 && crate::RocmQuantType::from_ggml(qtensor.dtype()).is_some()
2270 {
2271 return Ok(Self::QTensor(qtensor));
2272 }
2273 }
2274 #[cfg(feature = "vulkan")]
2280 {
2281 if qtensor.device().is_vulkan()
2282 && qtensor.shape().dims().len() == 3
2283 && vk_moe_kernel(qtensor.dtype()).is_some()
2284 {
2285 return Ok(Self::QTensor(qtensor));
2286 }
2287 }
2288 #[cfg(feature = "wgpu")]
2289 {
2290 if qtensor.device().is_wgpu()
2291 && qtensor.shape().dims().len() == 3
2292 && wgpu_moe_kernel(qtensor.dtype()).is_some()
2293 {
2294 return Ok(Self::QTensor(qtensor));
2295 }
2296 }
2297 let dequantize = match qtensor.dtype() {
2298 GgmlDType::F32 | GgmlDType::F16 | GgmlDType::BF16 | GgmlDType::I32 => true,
2299 _ => {
2302 qtensor.device().is_vulkan()
2303 || qtensor.device().is_wgpu()
2304 || qtensor.device().is_rocm()
2305 }
2306 };
2307 let t = if dequantize {
2308 if qtensor.device().is_rocm() {
2312 Self::TensorF16(qtensor.dequantize_f16(&qtensor.device())?)
2313 } else {
2314 Self::Tensor(qtensor.dequantize(&qtensor.device())?)
2315 }
2316 } else {
2317 Self::QTensor(qtensor)
2318 };
2319 Ok(t)
2320 }
2321
2322 pub fn from_qtensor(qtensor: QTensor) -> Result<Self> {
2323 Self::from_arc(std::sync::Arc::new(qtensor))
2324 }
2325
2326 pub fn dequantize_f16(&self) -> Result<Tensor> {
2327 match self {
2328 Self::QTensor(t) => t.dequantize_f16(&t.device()),
2329 Self::Tensor(t) => t.to_dtype(DType::F16),
2330 Self::TensorF16(t) => Ok(t.clone()),
2331 #[cfg(feature = "rocm")]
2332 Self::RocmQuant { qtensor, .. } => qtensor.dequantize_f16(&qtensor.device()),
2333 #[cfg(feature = "vulkan")]
2334 Self::VulkanQuant { qtensor, .. } => qtensor.dequantize_f16(&qtensor.device()),
2335 #[cfg(feature = "wgpu")]
2336 Self::WgpuQuant { qtensor, .. } => qtensor.dequantize_f16(&qtensor.device()),
2337 }
2338 }
2339
2340 pub fn forward_via_f16(&self, xs: &Tensor) -> Result<Tensor> {
2341 let w = self.dequantize_f16()?;
2342 let in_dtype = xs.dtype();
2343 let w = match *xs.dims() {
2344 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2345 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2346 _ => w.t()?,
2347 };
2348 xs.to_dtype(DType::F16)?.matmul(&w)?.to_dtype(in_dtype)
2349 }
2350
2351 pub fn indexed_moe_forward(&self, x: &Tensor, ids: &Tensor) -> Result<Tensor> {
2352 match self {
2353 Self::QTensor(t) => t.indexed_moe_forward(x, ids),
2354 #[cfg(feature = "rocm")]
2359 Self::RocmQuant {
2360 qtensor, wq, dtype, ..
2361 } if crate::RocmQuantType::from_ggml(*dtype).is_some() => {
2362 let qt = crate::RocmQuantType::from_ggml(*dtype).unwrap();
2363 let wbank = wq.as_ref();
2364 let (_e_cnt, n, k) = qtensor.shape().dims3()?;
2366 let (t, topk) = ids.dims2()?;
2367 let s = x.dim(1)?; let x_exp = if s == topk {
2369 x.clone()
2370 } else {
2371 x.broadcast_as((t, topk, k))?
2372 };
2373 let nrows = t * topk;
2374 let use_qmmq = t > 1 && qt.qmmq_capable();
2380 let x_flat = match x_exp.dtype() {
2381 DType::F16 | DType::F32 if use_qmmq => {
2385 x_exp.reshape((nrows, k))?.contiguous()?
2386 }
2387 _ if use_qmmq => x_exp
2388 .reshape((nrows, k))?
2389 .to_dtype(DType::F16)?
2390 .contiguous()?,
2391 DType::BF16 | DType::F16 => x_exp.reshape((nrows, k))?.contiguous()?,
2392 DType::F32 if qt.dp4a_active() => x_exp.reshape((nrows, k))?.contiguous()?,
2395 _ => x_exp
2396 .reshape((nrows, k))?
2397 .to_dtype(DType::F16)?
2398 .contiguous()?,
2399 };
2400 let out_dtype = x.dtype();
2401 let ids_u32 = ids
2406 .reshape((nrows,))?
2407 .to_dtype(crate::DType::U32)?
2408 .contiguous()?;
2409 let (xstore, _) = x_flat.storage_and_layout();
2410 let xr = match &*xstore {
2411 crate::Storage::Rocm(r) => r,
2412 _ => crate::bail!("rocm MoE: x not on rocm after contiguous()"),
2413 };
2414 let (idstore, _) = ids_u32.storage_and_layout();
2415 let idr = match &*idstore {
2416 crate::Storage::Rocm(r) => r,
2417 _ => crate::bail!("rocm MoE: ids not on rocm"),
2418 };
2419 let y = if use_qmmq {
2420 wbank
2421 .device
2422 .moe_qmmq_quant(qt, wbank, xr, idr, nrows, n, k)?
2423 } else {
2424 wbank
2425 .device
2426 .moe_matvec_quant(qt, wbank, xr, idr, nrows, n, k)?
2427 };
2428 let out = crate::tensor::from_storage(
2429 crate::Storage::Rocm(y),
2430 (nrows, n),
2431 crate::op::BackpropOp::none(),
2432 false,
2433 );
2434 out.reshape((t, topk, n))?.to_dtype(out_dtype)
2435 }
2436 #[cfg(feature = "rocm")]
2438 Self::RocmQuant { qtensor, .. } => qtensor.indexed_moe_forward(x, ids),
2439 #[cfg(feature = "vulkan")]
2440 Self::VulkanQuant { qtensor, .. } => qtensor.indexed_moe_forward(x, ids),
2441 #[cfg(feature = "wgpu")]
2442 Self::WgpuQuant { qtensor, .. } => qtensor.indexed_moe_forward(x, ids),
2443 _ => {
2444 panic!("Not implemented!")
2445 }
2446 }
2447 }
2448}
2449
2450impl crate::CustomOp1 for QTensor {
2451 fn name(&self) -> &'static str {
2452 "qmatmul"
2453 }
2454
2455 fn cpu_fwd(
2456 &self,
2457 storage: &crate::CpuStorage,
2458 layout: &crate::Layout,
2459 ) -> Result<(crate::CpuStorage, Shape)> {
2460 if !layout.is_contiguous() {
2461 crate::bail!("input tensor is not contiguous {layout:?}")
2462 }
2463 let src_shape = layout.shape();
2464 let (n, k) = self.shape.dims2()?;
2466 if src_shape.rank() < 2 {
2467 crate::bail!("input tensor has only one dimension {layout:?}")
2468 }
2469 let mut dst_shape = src_shape.dims().to_vec();
2470 let last_k = dst_shape.pop().unwrap();
2471 if last_k != k {
2472 crate::bail!("input tensor {layout:?} incompatible with {:?}", self.shape)
2473 }
2474 dst_shape.push(n);
2475 let dst_shape = Shape::from(dst_shape);
2476 #[allow(clippy::infallible_destructuring_match)]
2477 let self_storage = match &self.storage {
2478 QStorage::Cpu(storage) => storage,
2479 #[cfg(feature = "rocm")]
2480 QStorage::Rocm(..) => crate::bail!("Invalid storage"),
2481 #[cfg(feature = "vulkan")]
2482 QStorage::Vulkan(..) => crate::bail!("Invalid storage"),
2483 #[cfg(feature = "wgpu")]
2484 QStorage::Wgpu(..) => crate::bail!("Invalid storage"),
2485 QStorage::Metal(_) | QStorage::Cuda(_) | QStorage::Stream(_) => {
2486 crate::bail!("Invalid storage")
2487 }
2488 };
2489 match storage.dtype() {
2490 DType::F32 => {
2491 let slice = storage.as_slice::<f32>()?;
2492 let slice =
2493 &slice[layout.start_offset()..layout.start_offset() + src_shape.elem_count()];
2494 let mut dst_storage = vec![0f32; dst_shape.elem_count()];
2495 self_storage.matmul_t(
2496 (dst_shape.elem_count() / n, k, n),
2497 slice,
2498 &mut dst_storage,
2499 )?;
2500 Ok((crate::CpuStorage::F32(dst_storage), dst_shape))
2501 }
2502 DType::F16 => {
2503 let slice = storage.as_slice::<f16>()?;
2504 let slice =
2505 &slice[layout.start_offset()..layout.start_offset() + src_shape.elem_count()];
2506 let mut dst_storage = vec![f16::ZERO; dst_shape.elem_count()];
2507 self_storage.matmul_t_f16(
2508 (dst_shape.elem_count() / n, k, n),
2509 slice,
2510 &mut dst_storage,
2511 )?;
2512 Ok((crate::CpuStorage::F16(dst_storage), dst_shape))
2513 }
2514 _ => crate::bail!("Expected f32/f16"),
2515 }
2516 }
2517
2518 fn metal_fwd(
2519 &self,
2520 storage: &crate::MetalStorage,
2521 layout: &crate::Layout,
2522 ) -> Result<(crate::MetalStorage, Shape)> {
2523 let self_storage = match &self.storage {
2524 QStorage::Metal(metal) => metal,
2525 _ => unreachable!("Cannot call metal matmul on non metal QTensor"),
2526 };
2527 self_storage.fwd(&self.shape, storage, layout)
2528 }
2529
2530 fn cuda_fwd(
2531 &self,
2532 storage: &crate::CudaStorage,
2533 layout: &crate::Layout,
2534 ) -> Result<(crate::CudaStorage, Shape)> {
2535 let self_storage = match &self.storage {
2536 QStorage::Cuda(cuda) => cuda,
2537 _ => unreachable!("Cannot call cuda matmul on non cuda QTensor"),
2538 };
2539 self_storage.fwd(&self.shape, storage, layout)
2540 }
2541}
2542
2543fn dense_matmul(xs: &Tensor, w: &Tensor) -> Result<Tensor> {
2553 let k = *w.dims().last().unwrap();
2554 let rows = xs.elem_count() / k;
2555 if rows == 1 && xs.device().is_rocm() {
2556 let n = w.dim(0)?;
2557 #[cfg(feature = "rocm")]
2558 {
2559 let d = match xs.device() {
2563 Device::Rocm(d) => d.clone(),
2564 _ => unreachable!(),
2565 };
2566 let xs1 = xs.reshape((k,))?.to_dtype(w.dtype())?.contiguous()?;
2567 let w = w.contiguous()?;
2568 let (wstore, _) = w.storage_and_layout();
2569 let wr = match &*wstore {
2570 crate::Storage::Rocm(r) => r,
2571 _ => crate::bail!("dense_matmul: weight not on rocm"),
2572 };
2573 let (xstore, _) = xs1.storage_and_layout();
2574 let xr = match &*xstore {
2575 crate::Storage::Rocm(r) => r,
2576 _ => crate::bail!("dense_matmul: x not on rocm"),
2577 };
2578 let y = d.dense_gemv(wr, xr, n, k)?;
2579 let mut dims = xs.dims().to_vec();
2580 *dims.last_mut().unwrap() = n;
2581 return crate::tensor::from_storage(
2582 crate::Storage::Rocm(y),
2583 dims,
2584 crate::op::BackpropOp::none(),
2585 false,
2586 )
2587 .to_dtype(xs.dtype());
2588 }
2589 #[cfg(not(feature = "rocm"))]
2590 {
2591 let out = xs.reshape((1, k))?.broadcast_mul(w)?.sum(D::Minus1)?;
2592 let mut dims = xs.dims().to_vec();
2593 *dims.last_mut().unwrap() = n;
2594 return out.reshape(dims);
2595 }
2596 }
2597 let w = match *xs.dims() {
2598 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2599 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2600 _ => w.t()?,
2601 };
2602 xs.matmul(&w)
2603}
2604
2605#[cfg(feature = "vulkan")]
2620fn vulkan_prefill_gemm_max_rows(dtype: GgmlDType) -> usize {
2621 match dtype {
2622 GgmlDType::Q4_0 | GgmlDType::Q8_0 | GgmlDType::Q4K | GgmlDType::Q5K | GgmlDType::Q6K => {
2623 usize::MAX
2624 }
2625 _ => 0,
2626 }
2627}
2628
2629#[cfg(feature = "vulkan")]
2636fn vulkan_act_offset0(xs: &Tensor) -> Result<Tensor> {
2637 let xs = xs.contiguous()?;
2638 if xs.layout().start_offset() == 0 {
2639 Ok(xs)
2640 } else {
2641 xs.force_contiguous()
2642 }
2643}
2644
2645impl crate::Module for QMatMul {
2646 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
2647 match self {
2648 #[cfg(feature = "rocm")]
2649 Self::RocmQuant {
2650 qtensor,
2651 wq,
2652 dtype,
2653 n,
2654 k,
2655 } => {
2656 let xs_recovered = if xs.device().is_rocm() {
2660 None
2661 } else {
2662 Some(xs.to_device(&qtensor.device())?)
2663 };
2664 let xs = xs_recovered.as_ref().unwrap_or(xs);
2665 let rows: usize = xs.elem_count() / *k;
2666 #[cfg(feature = "rocm")]
2672 let unified_qt = crate::RocmQuantType::from_ggml(*dtype);
2673 #[cfg(not(feature = "rocm"))]
2674 let unified_qt: Option<()> = None;
2675 #[cfg(feature = "rocm")]
2680 let qmmq_ok = unified_qt.map(|qt| qt.qmmq_capable()).unwrap_or(false);
2681 #[cfg(not(feature = "rocm"))]
2682 let qmmq_ok = false;
2683 if rows == 1 && unified_qt.is_some() {
2684 #[cfg(feature = "rocm")]
2695 let keep_f32 = unified_qt.map(|qt| qt.dp4a_active()).unwrap_or(false);
2696 #[cfg(not(feature = "rocm"))]
2697 let keep_f32 = false;
2698 let xs = match xs.dtype() {
2699 DType::BF16 | DType::F16 => xs.contiguous()?,
2700 DType::F32 if keep_f32 => xs.contiguous()?,
2701 _ => xs.to_dtype(DType::F16)?.contiguous()?,
2702 };
2703 let d = match xs.device() {
2704 Device::Rocm(d) => d,
2705 _ => crate::bail!("RocmQuant input not on rocm"),
2706 };
2707 let y = {
2708 let (store, _) = xs.storage_and_layout();
2709 let xr = match &*store {
2710 crate::Storage::Rocm(r) => r,
2711 _ => crate::bail!("RocmQuant expected rocm storage"),
2712 };
2713 #[cfg(feature = "rocm")]
2714 {
2715 d.matvec_quant(unified_qt.unwrap(), wq, xr, *n, *k)?
2716 }
2717 #[cfg(not(feature = "rocm"))]
2718 {
2719 crate::bail!("rocm feature disabled")
2720 }
2721 };
2722 let mut dims = xs.dims().to_vec();
2723 let last = dims.len() - 1;
2724 dims[last] = *n;
2725 Ok(crate::tensor::from_storage(
2726 crate::Storage::Rocm(y),
2727 dims,
2728 crate::op::BackpropOp::none(),
2729 false,
2730 ))
2731 } else if let Some(qt) = unified_qt.filter(|_| qmmq_ok) {
2732 let xs = xs.to_dtype(DType::F16)?.contiguous()?;
2741 let d = match xs.device() {
2742 Device::Rocm(d) => d,
2743 _ => crate::bail!("RocmQuant input not on rocm"),
2744 };
2745 let m = xs.elem_count() / *k;
2746 let y = {
2747 let (store, _) = xs.storage_and_layout();
2748 let xr = match &*store {
2749 crate::Storage::Rocm(r) => r,
2750 _ => crate::bail!("RocmQuant expected rocm storage"),
2751 };
2752 #[cfg(feature = "rocm")]
2753 {
2754 d.qmmq_quant(qt, xr, wq, m, *n, *k)?
2755 }
2756 #[cfg(not(feature = "rocm"))]
2757 {
2758 let _ = qt;
2759 crate::bail!("rocm feature disabled")
2760 }
2761 };
2762 let mut dims = xs.dims().to_vec();
2763 let last = dims.len() - 1;
2764 dims[last] = *n;
2765 Ok(crate::tensor::from_storage(
2766 crate::Storage::Rocm(y),
2767 dims,
2768 crate::op::BackpropOp::none(),
2769 false,
2770 ))
2771 } else {
2772 let w = qtensor.dequantize_f16(&xs.device())?;
2776 let w = match *xs.dims() {
2777 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2778 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2779 _ => w.t()?,
2780 };
2781 xs.to_dtype(DType::F16)?.matmul(&w)
2782 }
2783 }
2784 #[cfg(feature = "vulkan")]
2785 Self::VulkanQuant {
2786 qtensor,
2787 wq,
2788 dtype,
2789 n,
2790 k,
2791 } => {
2792 let vdev = qtensor.device();
2797 let xs_on_vdev = if xs.device().same_device(&vdev) {
2798 None
2799 } else {
2800 Some(xs.to_device(&vdev)?)
2801 };
2802 let xs = xs_on_vdev.as_ref().unwrap_or(xs);
2803 let rows: usize = xs.elem_count() / *k;
2804 if rows == 1 {
2805 let xs = vulkan_act_offset0(xs)?;
2808 let d = match xs.device() {
2809 Device::Vulkan(d) => d,
2810 _ => crate::bail!("VulkanQuant input not on vulkan"),
2811 };
2812 let y = {
2813 let (store, _) = xs.storage_and_layout();
2814 let xv = match &*store {
2815 crate::Storage::Vulkan(v) => v,
2816 _ => crate::bail!("VulkanQuant expected vulkan storage"),
2817 };
2818 match dtype {
2819 GgmlDType::Q4_0 => d.matvec_q4_0_gpu(wq, xv, *n, *k)?,
2820 GgmlDType::Q8_0 => d.matvec_q8_gpu(wq, xv, *n, *k)?,
2823 GgmlDType::Q4K => d.matvec_q4k_gpu(wq, xv, *n, *k)?,
2824 GgmlDType::Q5K => d.matvec_q5k_gpu(wq, xv, *n, *k)?,
2825 GgmlDType::Q6K => d.matvec_q6k_gpu(wq, xv, *n, *k)?,
2826 GgmlDType::Q2K => d.matvec_q2k_gpu(wq, xv, *n, *k)?,
2827 GgmlDType::Q3K => d.matvec_q3k_gpu(wq, xv, *n, *k)?,
2828 GgmlDType::IQ4_XS => d.matvec_iq4xs_gpu(wq, xv, *n, *k)?,
2829 GgmlDType::IQ4_NL => d.matvec_iq4nl_gpu(wq, xv, *n, *k)?,
2830 GgmlDType::IQ2_XXS => d.matvec_iq2xxs_gpu(wq, xv, *n, *k)?,
2831 GgmlDType::IQ2_XS => d.matvec_iq2xs_gpu(wq, xv, *n, *k)?,
2832 GgmlDType::IQ1_M => d.matvec_iq1m_gpu(wq, xv, *n, *k)?,
2833 GgmlDType::IQ1_S => d.matvec_iq1s_gpu(wq, xv, *n, *k)?,
2834 GgmlDType::IQ3_S => d.matvec_iq3s_gpu(wq, xv, *n, *k)?,
2835 GgmlDType::IQ3_XXS => d.matvec_iq3xxs_gpu(wq, xv, *n, *k)?,
2836 GgmlDType::IQ2_S => d.matvec_iq2s_gpu(wq, xv, *n, *k)?,
2837 GgmlDType::TQ2_0 => d.matvec_tq2_0_gpu(wq, xv, *n, *k)?,
2838 other => crate::bail!("VulkanQuant: no native matvec for {other:?}"),
2839 }
2840 };
2841 let mut dims = xs.dims().to_vec();
2842 let last = dims.len() - 1;
2843 dims[last] = *n;
2844 Ok(crate::tensor::from_storage(
2845 crate::Storage::Vulkan(y),
2846 dims,
2847 crate::op::BackpropOp::none(),
2848 false,
2849 ))
2850 } else if rows <= vulkan_prefill_gemm_max_rows(*dtype) {
2851 let m = rows;
2860 let xs = vulkan_act_offset0(xs)?;
2861 let d = match xs.device() {
2862 Device::Vulkan(d) => d,
2863 _ => crate::bail!("VulkanQuant input not on vulkan"),
2864 };
2865 let y = {
2866 let (store, _) = xs.storage_and_layout();
2867 let xv = match &*store {
2868 crate::Storage::Vulkan(v) => v,
2869 _ => crate::bail!("VulkanQuant expected vulkan storage"),
2870 };
2871 match dtype {
2872 GgmlDType::Q4_0 => d.matmul_q4_0_gpu(wq, xv, m, *n, *k)?,
2873 GgmlDType::Q8_0 => d.matmul_q8_gpu(wq, xv, m, *n, *k)?,
2874 GgmlDType::Q4K
2879 if *n == 2048
2880 && *k == 2048
2881 && std::env::var_os("VK_MMQ_Q4K").is_some() =>
2882 {
2883 let bank = qtensor.vulkan_moe_bank_split(d, 1, *n, *k)?;
2884 let (xq, xsq, xsum) = d.quantize_act_q8(xv, m, *k)?;
2885 d.mmq_q4k_gpu(&xq, &xsq, &xsum, bank.as_ref(), m, *n)?
2886 }
2887 GgmlDType::Q4K => d.matmul_q4k_gpu(wq, xv, m, *n, *k)?,
2888 GgmlDType::Q5K => d.matmul_q5k_gpu(wq, xv, m, *n, *k)?,
2889 GgmlDType::Q6K => d.matmul_q6k_gpu(wq, xv, m, *n, *k)?,
2890 other => crate::bail!("VulkanQuant: no native matmul for {other:?}"),
2891 }
2892 };
2893 let mut dims = xs.dims().to_vec();
2894 let last = dims.len() - 1;
2895 dims[last] = *n;
2896 Ok(crate::tensor::from_storage(
2897 crate::Storage::Vulkan(y),
2898 dims,
2899 crate::op::BackpropOp::none(),
2900 false,
2901 ))
2902 } else {
2903 let w = qtensor.dequantize(&xs.device())?;
2908 let w = match *xs.dims() {
2909 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2910 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2911 _ => w.t()?,
2912 };
2913 xs.matmul(&w)
2914 }
2915 }
2916 #[cfg(feature = "wgpu")]
2917 Self::WgpuQuant {
2918 qtensor,
2919 wq,
2920 dtype,
2921 n,
2922 k,
2923 } => {
2924 let vdev = qtensor.device();
2928 let xs_on_vdev = if xs.device().same_device(&vdev) {
2929 None
2930 } else {
2931 Some(xs.to_device(&vdev)?)
2932 };
2933 let xs = xs_on_vdev.as_ref().unwrap_or(xs);
2934 let rows: usize = xs.elem_count() / *k;
2935 if rows == 1 {
2936 let xs = xs.contiguous()?;
2939 let d = match xs.device() {
2940 Device::Wgpu(d) => d,
2941 _ => crate::bail!("WgpuQuant input not on wgpu"),
2942 };
2943 let y = {
2944 let (store, _) = xs.storage_and_layout();
2945 let xv = match &*store {
2946 crate::Storage::Wgpu(v) => v,
2947 _ => crate::bail!("WgpuQuant expected wgpu storage"),
2948 };
2949 match dtype {
2950 GgmlDType::Q4_0 => d.matvec_q4_0_gpu(wq, xv, *n, *k)?,
2951 GgmlDType::Q8_0 => d.matvec_q8_0_gpu(wq, xv, *n, *k)?,
2952 GgmlDType::Q4K => d.matvec_q4k_gpu(wq, xv, *n, *k)?,
2953 other => crate::bail!("WgpuQuant: no native matvec for {other:?}"),
2954 }
2955 };
2956 let mut dims = xs.dims().to_vec();
2957 let last = dims.len() - 1;
2958 dims[last] = *n;
2959 Ok(crate::tensor::from_storage(
2960 crate::Storage::Wgpu(y),
2961 dims,
2962 crate::op::BackpropOp::none(),
2963 false,
2964 ))
2965 } else {
2966 let w = qtensor.dequantize(&xs.device())?;
2968 let w = match *xs.dims() {
2969 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2970 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2971 _ => w.t()?,
2972 };
2973 xs.matmul(&w)
2974 }
2975 }
2976 Self::QTensor(t) => xs.apply_op1_no_bwd(t.as_ref()),
2977 Self::Tensor(w) => dense_matmul(xs, w),
2978 Self::TensorF16(w) => {
2979 let in_dtype = xs.dtype();
2980 dense_matmul(&xs.to_dtype(DType::F16)?, w)?.to_dtype(in_dtype)
2981 }
2982 }
2983 }
2984}