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 banks: ResidentBanks,
91}
92
93impl Device {
94 fn qzeros(&self, elem_count: usize, dtype: GgmlDType) -> Result<QStorage> {
95 match self {
96 Device::Cpu => {
97 let storage = dtype.cpu_zeros(elem_count);
98 Ok(QStorage::Cpu(storage))
99 }
100 Device::Metal(metal) => {
101 let storage = metal::QMetalStorage::zeros(metal, elem_count, dtype)?;
102 Ok(QStorage::Metal(storage))
103 }
104 Device::Cuda(cuda) => {
105 let storage = cuda::QCudaStorage::zeros(cuda, elem_count, dtype)?;
106 Ok(QStorage::Cuda(storage))
107 }
108 #[cfg(feature = "rocm")]
109 Device::Rocm(d) => {
110 let storage = dtype.cpu_zeros(elem_count);
113 Ok(QStorage::Rocm(storage, d.clone()))
114 }
115 #[cfg(feature = "vulkan")]
116 Device::Vulkan(d) => {
117 let storage = dtype.cpu_zeros(elem_count);
120 Ok(QStorage::Vulkan(storage, d.clone()))
121 }
122 #[cfg(feature = "wgpu")]
123 Device::Wgpu(d) => {
124 let storage = dtype.cpu_zeros(elem_count);
127 Ok(QStorage::Wgpu(storage, d.clone()))
128 }
129 }
130 }
131}
132
133pub enum QStorage {
134 Cpu(Box<dyn QuantizedType>),
135 Metal(metal::QMetalStorage),
136 Cuda(cuda::QCudaStorage),
137 #[cfg(feature = "rocm")]
141 Rocm(Box<dyn QuantizedType>, crate::RocmDevice),
142 #[cfg(feature = "vulkan")]
146 Vulkan(Box<dyn QuantizedType>, crate::VulkanDevice),
147 #[cfg(feature = "wgpu")]
151 Wgpu(Box<dyn QuantizedType>, crate::WgpuDevice),
152 Stream(Arc<expert_stream::ExpertStreamBank>),
156}
157
158impl QStorage {
159 pub fn from_data(data: Cow<'_, [u8]>, device: &Device, dtype: GgmlDType) -> Result<Self> {
160 match device {
161 Device::Cpu => Ok(Self::Cpu(dtype.from_data(data))),
162 Device::Metal(d) => match dtype {
163 GgmlDType::F32 => metal::load_quantized(d, as_t_slice::<f32>(&data)),
164 GgmlDType::F16 => metal::load_quantized(d, as_t_slice::<f16>(&data)),
165 GgmlDType::Q4_0 => metal::load_quantized(d, as_t_slice::<BlockQ4_0>(&data)),
166 GgmlDType::Q4_1 => metal::load_quantized(d, as_t_slice::<BlockQ4_1>(&data)),
167 GgmlDType::Q5_0 => metal::load_quantized(d, as_t_slice::<BlockQ5_0>(&data)),
168 GgmlDType::Q5_1 => metal::load_quantized(d, as_t_slice::<BlockQ5_1>(&data)),
169 GgmlDType::Q8_0 => metal::load_quantized(d, as_t_slice::<BlockQ8_0>(&data)),
170 GgmlDType::Q8_1 => metal::load_quantized(d, as_t_slice::<BlockQ8_1>(&data)),
171 GgmlDType::Q2K => metal::load_quantized(d, as_t_slice::<BlockQ2K>(&data)),
172 GgmlDType::Q3K => metal::load_quantized(d, as_t_slice::<BlockQ3K>(&data)),
173 GgmlDType::Q4K => metal::load_quantized(d, as_t_slice::<BlockQ4K>(&data)),
174 GgmlDType::Q5K => metal::load_quantized(d, as_t_slice::<BlockQ5K>(&data)),
175 GgmlDType::Q6K => metal::load_quantized(d, as_t_slice::<BlockQ6K>(&data)),
176 GgmlDType::Q8K => metal::load_quantized(d, as_t_slice::<BlockQ8K>(&data)),
177 GgmlDType::IQ4_NL => metal::load_quantized(d, as_t_slice::<BlockIQ4nl>(&data)),
178 GgmlDType::IQ4_XS => metal::load_quantized(d, as_t_slice::<BlockIQ4xs>(&data)),
179 GgmlDType::MXFP4 => metal::load_quantized(d, as_t_slice::<BlockMXFP4>(&data)),
180 GgmlDType::BF16 => metal::load_quantized(d, as_t_slice::<bf16>(&data)),
181 GgmlDType::I32 => metal::load_quantized(d, as_t_slice::<i32>(&data)),
182 GgmlDType::IQ2_XXS => metal::load_quantized(d, as_t_slice::<BlockIQ2xxs>(&data)),
188 GgmlDType::IQ2_XS => metal::load_quantized(d, as_t_slice::<BlockIQ2xs>(&data)),
189 GgmlDType::IQ2_S => metal::load_quantized(d, as_t_slice::<BlockIQ2s>(&data)),
190 GgmlDType::IQ3_XXS => metal::load_quantized(d, as_t_slice::<BlockIQ3xxs>(&data)),
191 GgmlDType::IQ3_S => metal::load_quantized(d, as_t_slice::<BlockIQ3s>(&data)),
192 GgmlDType::IQ1_S => metal::load_quantized(d, as_t_slice::<BlockIQ1s>(&data)),
193 GgmlDType::IQ1_M => metal::load_quantized(d, as_t_slice::<BlockIQ1m>(&data)),
194 other => crate::bail!("{other:?} is not supported on the Metal backend"),
196 },
197 Device::Cuda(d) => match dtype {
198 GgmlDType::F32 => cuda::load_quantized(d, as_t_slice::<f32>(&data)),
199 GgmlDType::F16 => cuda::load_quantized(d, as_t_slice::<f16>(&data)),
200 GgmlDType::Q4_0 => cuda::load_quantized(d, as_t_slice::<BlockQ4_0>(&data)),
201 GgmlDType::Q4_1 => cuda::load_quantized(d, as_t_slice::<BlockQ4_1>(&data)),
202 GgmlDType::Q5_0 => cuda::load_quantized(d, as_t_slice::<BlockQ5_0>(&data)),
203 GgmlDType::Q5_1 => cuda::load_quantized(d, as_t_slice::<BlockQ5_1>(&data)),
204 GgmlDType::Q8_0 => cuda::load_quantized(d, as_t_slice::<BlockQ8_0>(&data)),
205 GgmlDType::Q8_1 => cuda::load_quantized(d, as_t_slice::<BlockQ8_1>(&data)),
206 GgmlDType::Q2K => cuda::load_quantized(d, as_t_slice::<BlockQ2K>(&data)),
207 GgmlDType::Q3K => cuda::load_quantized(d, as_t_slice::<BlockQ3K>(&data)),
208 GgmlDType::Q4K => cuda::load_quantized(d, as_t_slice::<BlockQ4K>(&data)),
209 GgmlDType::Q5K => cuda::load_quantized(d, as_t_slice::<BlockQ5K>(&data)),
210 GgmlDType::Q6K => cuda::load_quantized(d, as_t_slice::<BlockQ6K>(&data)),
211 GgmlDType::Q8K => cuda::load_quantized(d, as_t_slice::<BlockQ8K>(&data)),
212 GgmlDType::IQ4_NL => cuda::load_quantized(d, as_t_slice::<BlockIQ4nl>(&data)),
213 GgmlDType::IQ4_XS => cuda::load_quantized(d, as_t_slice::<BlockIQ4xs>(&data)),
214 GgmlDType::MXFP4 => cuda::load_quantized(d, as_t_slice::<BlockMXFP4>(&data)),
215 GgmlDType::BF16 => cuda::load_quantized(d, as_t_slice::<bf16>(&data)),
216 GgmlDType::I32 => cuda::load_quantized(d, as_t_slice::<i32>(&data)),
217 GgmlDType::IQ2_XXS => cuda::load_quantized(d, as_t_slice::<BlockIQ2xxs>(&data)),
225 GgmlDType::IQ2_XS => cuda::load_quantized(d, as_t_slice::<BlockIQ2xs>(&data)),
226 GgmlDType::IQ2_S => cuda::load_quantized(d, as_t_slice::<BlockIQ2s>(&data)),
227 GgmlDType::IQ3_XXS => cuda::load_quantized(d, as_t_slice::<BlockIQ3xxs>(&data)),
228 GgmlDType::IQ3_S => cuda::load_quantized(d, as_t_slice::<BlockIQ3s>(&data)),
229 GgmlDType::IQ1_S => cuda::load_quantized(d, as_t_slice::<BlockIQ1s>(&data)),
230 GgmlDType::IQ1_M => cuda::load_quantized(d, as_t_slice::<BlockIQ1m>(&data)),
231 GgmlDType::TQ1_0 => cuda::load_quantized(d, as_t_slice::<BlockTQ1_0>(&data)),
232 GgmlDType::TQ2_0 => cuda::load_quantized(d, as_t_slice::<BlockTQ2_0>(&data)),
233 GgmlDType::NVFP4 => cuda::load_quantized(d, as_t_slice::<BlockNVFP4>(&data)),
234 GgmlDType::Q1_0 => cuda::load_quantized(d, as_t_slice::<BlockQ1_0>(&data)),
235 },
236 #[cfg(feature = "rocm")]
237 Device::Rocm(d) => Ok(Self::Rocm(dtype.from_data(data), d.clone())),
238 #[cfg(feature = "vulkan")]
239 Device::Vulkan(d) => Ok(Self::Vulkan(dtype.from_data(data), d.clone())),
240 #[cfg(feature = "wgpu")]
241 Device::Wgpu(d) => Ok(Self::Wgpu(dtype.from_data(data), d.clone())),
242 }
243 }
244
245 fn block_size(&self) -> usize {
246 match self {
247 QStorage::Cpu(storage) => storage.block_size(),
248 QStorage::Metal(storage) => storage.dtype().block_size(),
249 QStorage::Cuda(storage) => storage.dtype().block_size(),
250 #[cfg(feature = "rocm")]
251 QStorage::Rocm(storage, _) => storage.block_size(),
252 #[cfg(feature = "vulkan")]
253 QStorage::Vulkan(storage, _) => storage.block_size(),
254 #[cfg(feature = "wgpu")]
255 QStorage::Wgpu(storage, _) => storage.block_size(),
256 QStorage::Stream(bank) => bank.dtype().block_size(),
257 }
258 }
259
260 fn dtype(&self) -> GgmlDType {
261 match self {
262 QStorage::Cpu(storage) => storage.dtype(),
263 QStorage::Metal(storage) => storage.dtype(),
264 QStorage::Cuda(storage) => storage.dtype(),
265 #[cfg(feature = "rocm")]
266 QStorage::Rocm(storage, _) => storage.dtype(),
267 #[cfg(feature = "vulkan")]
268 QStorage::Vulkan(storage, _) => storage.dtype(),
269 #[cfg(feature = "wgpu")]
270 QStorage::Wgpu(storage, _) => storage.dtype(),
271 QStorage::Stream(bank) => bank.dtype(),
272 }
273 }
274
275 fn device(&self) -> Device {
276 match self {
277 QStorage::Cpu(_storage) => Device::Cpu,
278 QStorage::Metal(storage) => Device::Metal(storage.device().clone()),
279 QStorage::Cuda(storage) => Device::Cuda(storage.device().clone()),
280 #[cfg(feature = "rocm")]
281 QStorage::Rocm(_storage, device) => Device::Rocm(device.clone()),
282 #[cfg(feature = "vulkan")]
283 QStorage::Vulkan(_storage, device) => Device::Vulkan(device.clone()),
284 #[cfg(feature = "wgpu")]
285 QStorage::Wgpu(_storage, device) => Device::Wgpu(device.clone()),
286 QStorage::Stream(_) => Device::Cpu,
287 }
288 }
289
290 fn size_in_bytes(&self) -> usize {
291 match self {
292 QStorage::Cpu(storage) => storage.storage_size_in_bytes(),
293 QStorage::Metal(storage) => storage.storage_size_in_bytes(),
294 QStorage::Cuda(storage) => storage.storage_size_in_bytes(),
295 #[cfg(feature = "rocm")]
296 QStorage::Rocm(storage, _) => storage.storage_size_in_bytes(),
297 #[cfg(feature = "vulkan")]
298 QStorage::Vulkan(storage, _) => storage.storage_size_in_bytes(),
299 #[cfg(feature = "wgpu")]
300 QStorage::Wgpu(storage, _) => storage.storage_size_in_bytes(),
301 QStorage::Stream(bank) => bank.logical_bytes(),
302 }
303 }
304
305 fn quantize(&mut self, src: &Storage) -> Result<()> {
306 match (self, src) {
307 (QStorage::Cpu(storage), Storage::Cpu(src)) => {
308 storage.from_float(src.as_slice::<f32>()?);
309 }
310 (QStorage::Metal(storage), Storage::Metal(src)) => storage.quantize(src)?,
311 (QStorage::Cuda(storage), Storage::Cuda(src)) => storage.quantize(src)?,
312 _ => crate::bail!("Invalid quantize storage locations do not match"),
313 }
314 Ok(())
315 }
316
317 fn quantize_imatrix(
318 &mut self,
319 src: &Storage,
320 imatrix_weights: &[f32],
321 n_per_row: usize,
322 ) -> Result<()> {
323 match (self, src) {
324 (QStorage::Cpu(storage), Storage::Cpu(src)) => {
325 storage.from_float_imatrix(src.as_slice::<f32>()?, imatrix_weights, n_per_row);
326 }
327 (QStorage::Metal(storage), Storage::Metal(src)) => {
328 storage.quantize_imatrix(src, imatrix_weights, n_per_row)?
329 }
330 (QStorage::Cuda(storage), Storage::Cuda(src)) => {
331 storage.quantize_imatrix(src, imatrix_weights, n_per_row)?
332 }
333 _ => crate::bail!("Invalid quantize storage locations do not match"),
334 }
335 Ok(())
336 }
337
338 fn quantize_onto(&mut self, src: &Storage) -> Result<()> {
339 match (self, src) {
340 (QStorage::Cpu(storage), Storage::Cpu(src)) => {
341 storage.from_float(src.as_slice::<f32>()?);
342 }
343 (QStorage::Metal(storage), Storage::Cpu(src)) => storage.quantize_onto(src)?,
344 (QStorage::Cuda(storage), Storage::Cpu(src)) => storage.quantize_onto(src)?,
345 _ => crate::bail!("Invalid quantize source storage locations: not on cpu"),
346 }
347 Ok(())
348 }
349
350 fn quantize_imatrix_onto(
351 &mut self,
352 src: &Storage,
353 imatrix_weights: &[f32],
354 n_per_row: usize,
355 ) -> Result<()> {
356 match (self, src) {
357 (QStorage::Cpu(storage), Storage::Cpu(src)) => {
358 storage.from_float_imatrix(src.as_slice::<f32>()?, imatrix_weights, n_per_row);
359 }
360 (QStorage::Metal(storage), Storage::Cpu(src)) => {
361 storage.quantize_imatrix_onto(src, imatrix_weights, n_per_row)?
362 }
363 (QStorage::Cuda(storage), Storage::Cpu(src)) => {
364 storage.quantize_imatrix_onto(src, imatrix_weights, n_per_row)?
365 }
366 _ => crate::bail!("Invalid quantize storage locations do not match"),
367 }
368 Ok(())
369 }
370
371 fn dequantize(&self, elem_count: usize) -> Result<Storage> {
372 match self {
373 QStorage::Cpu(storage) => Ok(Storage::Cpu(storage.dequantize(elem_count)?)),
374 QStorage::Metal(storage) => Ok(Storage::Metal(storage.dequantize(elem_count)?)),
375 QStorage::Cuda(storage) => Ok(Storage::Cuda(storage.dequantize(elem_count)?)),
376 #[cfg(feature = "rocm")]
377 QStorage::Rocm(storage, device) => {
378 use crate::backend::BackendDevice;
380 let cpu = storage.dequantize(elem_count)?;
381 Ok(Storage::Rocm(device.storage_from_cpu_storage(&cpu)?))
382 }
383 #[cfg(feature = "vulkan")]
384 QStorage::Vulkan(storage, device) => {
385 let cpu = storage.dequantize(elem_count)?;
387 Ok(Storage::Vulkan(device.upload_f32(cpu.as_slice::<f32>()?)?))
388 }
389 #[cfg(feature = "wgpu")]
390 QStorage::Wgpu(storage, device) => {
391 let cpu = storage.dequantize(elem_count)?;
393 Ok(Storage::Wgpu(device.upload_f32(cpu.as_slice::<f32>()?)?))
394 }
395 QStorage::Stream(_) => {
396 crate::bail!("streaming expert bank has no whole-tensor dequantize; consume it via indexed_moe_forward")
397 }
398 }
399 }
400
401 fn data(&self) -> Result<Cow<'_, [u8]>> {
402 match self {
403 QStorage::Cpu(storage) => {
404 let data_ptr = storage.as_ptr();
405 let size_in_bytes = storage.storage_size_in_bytes();
406 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
407 Ok(Cow::from(data))
408 }
409 QStorage::Cuda(storage) => Ok(Cow::from(storage.data()?)),
410 QStorage::Metal(storage) => Ok(Cow::from(storage.data()?)),
411 #[cfg(feature = "rocm")]
412 QStorage::Rocm(storage, _) => {
413 let data_ptr = storage.as_ptr();
414 let size_in_bytes = storage.storage_size_in_bytes();
415 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
416 Ok(Cow::from(data))
417 }
418 #[cfg(feature = "vulkan")]
419 QStorage::Vulkan(storage, _) => {
420 let data_ptr = storage.as_ptr();
421 let size_in_bytes = storage.storage_size_in_bytes();
422 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
423 Ok(Cow::from(data))
424 }
425 #[cfg(feature = "wgpu")]
426 QStorage::Wgpu(storage, _) => {
427 let data_ptr = storage.as_ptr();
428 let size_in_bytes = storage.storage_size_in_bytes();
429 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
430 Ok(Cow::from(data))
431 }
432 QStorage::Stream(_) => {
433 crate::bail!("streaming expert bank is not resident; consume it via indexed_moe_forward")
434 }
435 }
436 }
437
438 pub fn device_ptr(&self) -> Result<*const u8> {
439 match self {
440 QStorage::Cuda(storage) => storage.device_ptr(),
441 #[cfg(feature = "rocm")]
442 QStorage::Rocm(..) => crate::bail!("not implemented"),
443 #[cfg(feature = "vulkan")]
444 QStorage::Vulkan(..) => crate::bail!("not implemented"),
445 #[cfg(feature = "wgpu")]
446 QStorage::Wgpu(..) => crate::bail!("not implemented"),
447 QStorage::Metal(_) | QStorage::Cpu(_) | QStorage::Stream(_) => {
448 crate::bail!("not implemented");
449 }
450 }
451 }
452
453 #[cfg(feature = "cuda")]
454 pub fn device_ptr_with_guard<'a>(
455 &'a self,
456 stream: &'a crate::cuda_backend::cudarc::driver::CudaStream,
457 ) -> Result<(
458 *const u8,
459 crate::cuda_backend::cudarc::driver::SyncOnDrop<'a>,
460 )> {
461 match self {
462 QStorage::Cuda(storage) => storage.device_ptr_with_guard(stream),
463 QStorage::Metal(_) | QStorage::Cpu(_) | QStorage::Stream(_) => {
464 crate::bail!("not implemented");
465 }
466 }
467 }
468}
469
470#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
471pub enum GgmlDType {
472 F32,
473 F16,
474 BF16,
475 I32,
478 Q4_0,
479 Q4_1,
480 Q5_0,
481 Q5_1,
482 Q8_0,
483 Q8_1,
484 Q2K,
485 Q3K,
486 Q4K,
487 Q5K,
488 Q6K,
489 Q8K,
490 #[allow(non_camel_case_types)]
491 IQ4_NL,
492 #[allow(non_camel_case_types)]
493 IQ4_XS,
494 MXFP4,
496 #[allow(non_camel_case_types)]
498 IQ2_XXS,
499 #[allow(non_camel_case_types)]
500 IQ2_XS,
501 #[allow(non_camel_case_types)]
502 IQ3_XXS,
503 #[allow(non_camel_case_types)]
504 IQ1_S,
505 #[allow(non_camel_case_types)]
506 IQ3_S,
507 #[allow(non_camel_case_types)]
508 IQ2_S,
509 #[allow(non_camel_case_types)]
510 IQ1_M,
511 TQ1_0,
512 TQ2_0,
513 NVFP4,
514 Q1_0,
515}
516
517use crate::for_each_quant;
529
530macro_rules! gen_from_u32 {
531 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
532 pub(crate) fn from_u32(u: u32) -> Result<Self> {
533 let dtype = match u {
534 0 => Self::F32,
535 1 => Self::F16,
536 30 => Self::BF16,
537 26 => Self::I32,
538 $( $id => Self::$v, )+
539 _ => crate::bail!("unknown dtype for tensor {u}"),
540 };
541 Ok(dtype)
542 }
543 };
544}
545
546macro_rules! gen_to_u32 {
547 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
548 pub fn to_u32(self) -> u32 {
552 match self {
553 Self::F32 => 0,
554 Self::F16 => 1,
555 Self::BF16 => 30,
556 Self::I32 => 26,
557 $( Self::$v => $id, )+
558 }
559 }
560 };
561}
562
563macro_rules! gen_cpu_zeros {
564 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
565 pub fn cpu_zeros(&self, elem_count: usize) -> Box<dyn QuantizedType> {
567 match self {
568 Self::F32 => Box::new(vec![f32::zeros(); elem_count]),
569 Self::F16 => Box::new(vec![f16::zeros(); elem_count]),
570 Self::BF16 => Box::new(vec![bf16::zeros(); elem_count]),
571 Self::I32 => Box::new(vec![0i32; elem_count]),
572 $( Self::$v => Box::new(vec![<$b>::zeros(); elem_count / <$b>::BLCK_SIZE]), )+
573 }
574 }
575 };
576}
577
578macro_rules! gen_from_data {
579 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
580 pub fn from_data(&self, data: Cow<'_, [u8]>) -> Box<dyn QuantizedType> {
581 match self {
582 Self::F32 => Box::new(as_t_slice::<f32>(&data).to_vec()),
583 Self::F16 => Box::new(as_t_slice::<f16>(&data).to_vec()),
584 Self::BF16 => Box::new(as_t_slice::<bf16>(&data).to_vec()),
585 Self::I32 => Box::new(as_t_slice::<i32>(&data).to_vec()),
586 $( Self::$v => Box::new(as_t_slice::<$b>(&data).to_vec()), )+
587 }
588 }
589 };
590}
591
592macro_rules! gen_type_size {
593 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
594 pub fn type_size(&self) -> usize {
596 use k_quants::*;
597 match self {
598 Self::F32 => 4,
599 Self::F16 | Self::BF16 => 2,
600 Self::I32 => 4,
601 $( Self::$v => std::mem::size_of::<$b>(), )+
602 }
603 }
604 };
605}
606
607macro_rules! gen_type_align {
608 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
609 pub fn type_align(&self) -> usize {
614 use k_quants::*;
615 match self {
616 Self::F32 => std::mem::align_of::<f32>(),
617 Self::F16 | Self::BF16 => std::mem::align_of::<f16>(),
618 Self::I32 => std::mem::align_of::<i32>(),
619 $( Self::$v => std::mem::align_of::<$b>(), )+
620 }
621 }
622 };
623}
624
625macro_rules! gen_from_mmap {
626 ($($v:ident => $b:ident @ $id:literal),+ $(,)?) => {
627 #[allow(clippy::wrong_self_convention)] pub(crate) fn from_mmap(
634 &self,
635 mmap: Arc<memmap2::Mmap>,
636 offset: usize,
637 n_blocks: usize,
638 ) -> Box<dyn QuantizedType> {
639 match self {
640 Self::F32 => Box::new(QMmap::<f32>::new(mmap, offset, n_blocks)),
641 Self::F16 => Box::new(QMmap::<f16>::new(mmap, offset, n_blocks)),
642 Self::BF16 => Box::new(QMmap::<bf16>::new(mmap, offset, n_blocks)),
643 Self::I32 => Box::new(QMmap::<i32>::new(mmap, offset, n_blocks)),
644 $( Self::$v => Box::new(QMmap::<$b>::new(mmap, offset, n_blocks)), )+
645 }
646 }
647 };
648}
649
650impl GgmlDType {
651 for_each_quant!(gen_from_u32);
652 for_each_quant!(gen_to_u32);
653 for_each_quant!(gen_cpu_zeros);
654 for_each_quant!(gen_from_data);
655 for_each_quant!(gen_from_mmap);
656 for_each_quant!(gen_type_size);
657 for_each_quant!(gen_type_align);
658
659 pub fn block_size(&self) -> usize {
661 match self {
662 Self::F32 => 1,
663 Self::F16 | Self::BF16 => 1,
664 Self::I32 => 1,
665 Self::Q4_0 => k_quants::QK4_0,
666 Self::Q4_1 => k_quants::QK4_1,
667 Self::Q5_0 => k_quants::QK5_0,
668 Self::Q5_1 => k_quants::QK5_1,
669 Self::Q8_0 => k_quants::QK8_0,
670 Self::Q8_1 => k_quants::QK8_1,
671 Self::IQ4_NL => k_quants::QK4_NL,
672 Self::MXFP4 => k_quants::QK_MXFP4,
673 Self::Q1_0 => iq_quants::QK1_0,
674 Self::NVFP4 => iq_quants::QK_NVFP4,
675 Self::Q2K
676 | Self::Q3K
677 | Self::Q4K
678 | Self::Q5K
679 | Self::Q6K
680 | Self::Q8K
681 | Self::IQ4_XS
682 | Self::IQ2_XXS
683 | Self::IQ2_XS
684 | Self::IQ3_XXS
685 | Self::IQ1_S
686 | Self::IQ3_S
687 | Self::IQ2_S
688 | Self::IQ1_M
689 | Self::TQ1_0
690 | Self::TQ2_0 => k_quants::QK_K,
691 }
692 }
693}
694
695pub trait QuantizedType: Send + Sync {
697 fn dtype(&self) -> GgmlDType;
698 fn matmul_t(&self, mkn: (usize, usize, usize), lhs: &[f32], dst: &mut [f32]) -> Result<()>;
699 fn matmul_t_f16(&self, mkn: (usize, usize, usize), lhs: &[f16], dst: &mut [f16]) -> Result<()>;
700 fn dequantize(&self, elem_count: usize) -> Result<CpuStorage>;
701 fn storage_size_in_bytes(&self) -> usize;
702 fn as_ptr(&self) -> *const u8;
703 fn block_size(&self) -> usize;
704 #[allow(clippy::wrong_self_convention)]
705 fn from_float(&mut self, xs: &[f32]);
706 #[allow(clippy::wrong_self_convention)]
707 fn from_float_imatrix(&mut self, xs: &[f32], imatrix_weights: &[f32], n_per_row: usize);
708 fn size(&self) -> usize;
709}
710
711impl<T: k_quants::GgmlType + Send + Sync> QuantizedType for Vec<T> {
712 fn matmul_t(&self, mkn: (usize, usize, usize), lhs: &[f32], dst: &mut [f32]) -> Result<()> {
713 k_quants::matmul(mkn, lhs, self.as_slice(), dst)
714 }
715 fn matmul_t_f16(&self, mkn: (usize, usize, usize), lhs: &[f16], dst: &mut [f16]) -> Result<()> {
716 k_quants::matmul_f16(mkn, lhs, self.as_slice(), dst)
717 }
718
719 fn size(&self) -> usize {
720 self.len() * core::mem::size_of::<T>()
721 }
722
723 fn from_float(&mut self, xs: &[f32]) {
724 T::from_float(xs, self)
725 }
726
727 fn from_float_imatrix(&mut self, xs: &[f32], imatrix_weights: &[f32], n_per_row: usize) {
728 T::from_float_imatrix(xs, self, imatrix_weights, n_per_row)
729 }
730
731 fn dtype(&self) -> GgmlDType {
732 T::DTYPE
733 }
734
735 fn block_size(&self) -> usize {
736 T::BLCK_SIZE
737 }
738
739 fn dequantize(&self, elem_count: usize) -> Result<CpuStorage> {
740 let mut ys = vec![0.0f32; elem_count];
741 T::to_float(self.as_slice(), &mut ys);
742 Ok(CpuStorage::F32(ys))
743 }
744
745 fn storage_size_in_bytes(&self) -> usize {
746 self.len() * std::mem::size_of::<T>()
747 }
748
749 fn as_ptr(&self) -> *const u8 {
750 self.as_ptr() as *const u8
751 }
752}
753
754pub struct QMmap<T> {
767 mmap: Arc<memmap2::Mmap>,
768 offset: usize,
770 n_blocks: usize,
772 _t: std::marker::PhantomData<T>,
773}
774
775impl<T> QMmap<T> {
776 fn new(mmap: Arc<memmap2::Mmap>, offset: usize, n_blocks: usize) -> Self {
777 Self {
778 mmap,
779 offset,
780 n_blocks,
781 _t: std::marker::PhantomData,
782 }
783 }
784
785 #[inline]
790 fn as_slice(&self) -> &[T] {
791 let len = self.n_blocks * std::mem::size_of::<T>();
792 as_t_slice::<T>(&self.mmap[self.offset..self.offset + len])
793 }
794}
795
796impl<T: k_quants::GgmlType + Send + Sync> QuantizedType for QMmap<T> {
797 fn matmul_t(&self, mkn: (usize, usize, usize), lhs: &[f32], dst: &mut [f32]) -> Result<()> {
798 k_quants::matmul(mkn, lhs, self.as_slice(), dst)
799 }
800
801 fn matmul_t_f16(&self, mkn: (usize, usize, usize), lhs: &[f16], dst: &mut [f16]) -> Result<()> {
802 k_quants::matmul_f16(mkn, lhs, self.as_slice(), dst)
803 }
804
805 fn size(&self) -> usize {
806 self.n_blocks * std::mem::size_of::<T>()
807 }
808
809 fn from_float(&mut self, _xs: &[f32]) {
810 panic!("QMmap is read-only: cannot quantize into a memory-mapped weight region")
811 }
812
813 fn from_float_imatrix(&mut self, _xs: &[f32], _imatrix_weights: &[f32], _n_per_row: usize) {
814 panic!("QMmap is read-only: cannot quantize into a memory-mapped weight region")
815 }
816
817 fn dtype(&self) -> GgmlDType {
818 T::DTYPE
819 }
820
821 fn block_size(&self) -> usize {
822 T::BLCK_SIZE
823 }
824
825 fn dequantize(&self, elem_count: usize) -> Result<CpuStorage> {
826 let mut ys = vec![0.0f32; elem_count];
827 T::to_float(self.as_slice(), &mut ys);
828 Ok(CpuStorage::F32(ys))
829 }
830
831 fn storage_size_in_bytes(&self) -> usize {
832 self.n_blocks * std::mem::size_of::<T>()
833 }
834
835 fn as_ptr(&self) -> *const u8 {
836 self.as_slice().as_ptr() as *const u8
837 }
838}
839
840impl std::fmt::Debug for QTensor {
841 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
842 write!(f, "QTensor[{:?}; {:?}]", self.shape, self.dtype())
843 }
844}
845
846fn check_shape(shape: &Shape, block_size: usize) -> Result<()> {
847 let dims = shape.dims();
848 if dims.is_empty() {
849 crate::bail!("scalar tensor cannot be quantized {shape:?}")
850 }
851 if !dims[dims.len() - 1].is_multiple_of(block_size) {
852 crate::bail!(
853 "quantized tensor must have their last dim divisible by block size {shape:?} {}",
854 block_size
855 )
856 }
857 Ok(())
858}
859
860impl QTensor {
861 fn make(storage: QStorage, shape: Shape) -> Self {
864 Self {
865 storage,
866 shape,
867 banks: ResidentBanks::default(),
868 }
869 }
870
871 pub fn new<S: Into<Shape>>(storage: QStorage, shape: S) -> Result<Self> {
872 let shape = shape.into();
873 check_shape(&shape, storage.block_size())?;
874 Ok(Self::make(storage, shape))
875 }
876
877 pub fn quantize(src: &Tensor, dtype: GgmlDType) -> Result<Self> {
878 let shape = src.shape();
879 let block_size = dtype.block_size();
880 check_shape(shape, block_size)?;
881 let src = src.to_dtype(crate::DType::F32)?.flatten_all()?;
882 let elem_count = shape.elem_count();
883 if !elem_count.is_multiple_of(block_size) {
884 crate::bail!(
885 "tensor size ({shape:?}) is not divisible by block size {}",
886 block_size
887 )
888 }
889 let mut storage = src.device().qzeros(elem_count, dtype)?;
890 storage.quantize(&src.storage())?;
891 Ok(Self::make(storage, shape.clone()))
892 }
893
894 pub fn quantize_imatrix(
895 src: &Tensor,
896 imatrix_weights: &[f32],
897 dtype: GgmlDType,
898 ) -> Result<Self> {
899 let n_per_row = src.dim(D::Minus1)?;
902 if imatrix_weights.len() != n_per_row {
903 crate::bail!(
904 "imatrix weights must have the same length {} as the last dim of src {}",
905 imatrix_weights.len(),
906 src.dim(D::Minus1)?
907 );
908 }
909
910 let shape = src.shape();
911 let block_size = dtype.block_size();
912 check_shape(shape, block_size)?;
913 let src = src.to_dtype(crate::DType::F32)?.flatten_all()?;
914 let elem_count = shape.elem_count();
915 if !elem_count.is_multiple_of(block_size) {
916 crate::bail!(
917 "tensor size ({shape:?}) is not divisible by block size {}",
918 block_size
919 );
920 }
921 let mut storage = src.device().qzeros(elem_count, dtype)?;
922 storage.quantize_imatrix(&src.storage(), imatrix_weights, n_per_row)?;
923 Ok(Self::make(storage, shape.clone()))
924 }
925
926 pub fn quantize_imatrix_onto(
928 src: &Tensor,
929 imatrix_weights: &[f32],
930 dtype: GgmlDType,
931 dev: &Device,
932 ) -> Result<Self> {
933 if !src.device().is_cpu() {
934 crate::bail!(
935 "`quantize_onto` expects a `src` to be on the cpu, got {:?}.",
936 src.device()
937 )
938 }
939 let n_per_row = src.dim(D::Minus1)?;
942 if imatrix_weights.len() != n_per_row {
943 crate::bail!(
944 "imatrix weights must have the same length {} as the last dim of src {}",
945 imatrix_weights.len(),
946 src.dim(D::Minus1)?
947 );
948 }
949 let shape = src.shape();
950 let block_size = dtype.block_size();
951 check_shape(shape, block_size)?;
952 let src = src.to_dtype(crate::DType::F32)?.flatten_all()?;
953 let elem_count = shape.elem_count();
954 if !elem_count.is_multiple_of(block_size) {
955 crate::bail!(
956 "tensor size ({shape:?}) is not divisible by block size {}",
957 block_size
958 )
959 }
960 let mut storage = dev.qzeros(elem_count, dtype)?;
962 storage.quantize_imatrix_onto(&src.storage(), imatrix_weights, n_per_row)?;
963 Ok(Self::make(storage, shape.clone()))
964 }
965
966 pub fn quantize_onto(src: &Tensor, dtype: GgmlDType, dev: &Device) -> Result<Self> {
968 if !src.device().is_cpu() {
969 crate::bail!(
970 "`quantize_onto` expects a `src` to be on the cpu, got {:?}.",
971 src.device()
972 )
973 }
974 let shape = src.shape();
975 let block_size = dtype.block_size();
976 check_shape(shape, block_size)?;
977 let src = src.to_dtype(crate::DType::F32)?.flatten_all()?;
978 let elem_count = shape.elem_count();
979 if !elem_count.is_multiple_of(block_size) {
980 crate::bail!(
981 "tensor size ({shape:?}) is not divisible by block size {}",
982 block_size
983 )
984 }
985 let mut storage = dev.qzeros(elem_count, dtype)?;
987 storage.quantize_onto(&src.storage())?;
988 Ok(Self::make(storage, shape.clone()))
989 }
990
991 pub fn dtype(&self) -> GgmlDType {
992 self.storage.dtype()
993 }
994
995 pub fn device(&self) -> Device {
996 self.storage.device()
997 }
998
999 pub fn rank(&self) -> usize {
1000 self.shape.rank()
1001 }
1002
1003 pub fn shape(&self) -> &Shape {
1004 &self.shape
1005 }
1006
1007 pub fn dequantize(&self, device: &Device) -> Result<Tensor> {
1008 let storage = self.storage.dequantize(self.shape.elem_count())?;
1009 let none = crate::op::BackpropOp::none();
1010 crate::tensor::from_storage(storage, self.shape.clone(), none, false).to_device(device)
1011 }
1012
1013 pub fn dequantize_f16(&self, device: &Device) -> Result<Tensor> {
1014 match &self.storage {
1017 QStorage::Cuda(s) => {
1018 let s = s.dequantize_f16(self.shape.elem_count())?;
1019 let none = crate::op::BackpropOp::none();
1020 crate::tensor::from_storage(Storage::Cuda(s), self.shape.clone(), none, false)
1021 .to_device(device)
1022 }
1023 _ => {
1024 let s = self.dequantize(device)?.to_dtype(crate::DType::F16)?;
1025 Ok(s)
1026 }
1027 }
1028 }
1029
1030 pub fn storage_size_in_bytes(&self) -> usize {
1031 self.storage.size_in_bytes()
1032 }
1033
1034 pub fn data(&self) -> Result<Cow<'_, [u8]>> {
1035 self.storage.data()
1036 }
1037
1038 #[cfg(feature = "rocm")]
1042 fn rocm_moe_bank(&self, dev: &crate::RocmDevice) -> Result<std::sync::Arc<crate::RocmStorage>> {
1043 use crate::backend::BackendDevice;
1044 let bank = self.data()?;
1045 cache_or_upload(&self.banks.rocm, bank.as_ref(), |b| {
1046 dev.storage_from_slice(b)
1047 })
1048 }
1049
1050 #[cfg(feature = "vulkan")]
1054 fn vulkan_moe_bank(
1055 &self,
1056 dev: &crate::VulkanDevice,
1057 e_cnt: usize,
1058 n: usize,
1059 k: usize,
1060 ) -> Result<std::sync::Arc<crate::VulkanStorage>> {
1061 let bank = self.data()?;
1062 let dt = self.storage.dtype();
1063 cache_or_upload(&self.banks.vulkan, bank.as_ref(), |b| match dt {
1064 GgmlDType::Q8_0 => dev.quantize_q8_blocks(b, e_cnt * n, k),
1065 GgmlDType::Q6K => dev.quantize_q6k(b, e_cnt * n, k),
1066 _ => dev.upload_qweight(b),
1067 })
1068 }
1069
1070 #[cfg(feature = "vulkan")]
1074 fn vulkan_moe_bank_split(
1075 &self,
1076 dev: &crate::VulkanDevice,
1077 e_cnt: usize,
1078 n: usize,
1079 k: usize,
1080 ) -> Result<std::sync::Arc<crate::vulkan::MoeBankSplit>> {
1081 let bank = self.data()?;
1082 let dt = self.storage.dtype();
1083 cache_or_upload(&self.banks.vulkan_split, bank.as_ref(), |b| match dt {
1084 GgmlDType::Q4K => dev.quantize_q4k_split(b, e_cnt * n, k),
1085 GgmlDType::Q6K => dev.quantize_q6k_split(b, e_cnt * n, k),
1086 _ => crate::bail!("vulkan_moe_bank_split: unsupported dtype {dt:?}"),
1087 })
1088 }
1089
1090 #[cfg(feature = "wgpu")]
1093 fn wgpu_moe_bank(
1094 &self,
1095 dev: &crate::WgpuDevice,
1096 ) -> Result<std::sync::Arc<crate::WgpuStorage>> {
1097 let bank = self.data()?;
1098 cache_or_upload(&self.banks.wgpu, bank.as_ref(), |b| dev.upload_qweight(b))
1099 }
1100
1101 pub fn indexed_moe_forward(&self, x: &Tensor, ids: &Tensor) -> Result<Tensor> {
1102 let ids = &ids.contiguous()?;
1105 match &self.storage {
1106 QStorage::Cuda(s) if cuda::QCudaStorage::supports_indexed_moe(s.dtype()) => {
1112 let out_dtype = x.dtype();
1117 let x = x.to_dtype(crate::DType::F32)?.contiguous()?;
1118 let (x_guard, x_l) = x.storage_and_layout();
1121 let (ids_guard, ids_l) = ids.storage_and_layout();
1122 match (&*x_guard, &*ids_guard) {
1123 (Storage::Cuda(x_storage), Storage::Cuda(ids_storage)) => {
1124 let (storage, out_shape) = s.indexed_moe_forward(
1125 self.shape(),
1126 x_storage,
1127 x_l,
1128 ids_storage,
1129 ids_l,
1130 )?;
1131 crate::tensor::from_storage(
1132 Storage::Cuda(storage),
1133 out_shape,
1134 crate::op::BackpropOp::none(),
1135 false,
1136 )
1137 .to_dtype(out_dtype)
1138 }
1139 _ => {
1140 panic!("Non-cuda indexed_moe_forward is not implemented!");
1141 }
1142 }
1143 }
1144 #[cfg(feature = "cuda")]
1150 QStorage::Cuda(s) if cuda::QCudaStorage::supports_iquant_moe(s.dtype()) => {
1151 let out_dtype = x.dtype();
1152 let (_e_cnt, n, k) = self.shape().dims3()?;
1153 let (t, topk) = ids.dims2()?;
1154 let nrows = t * topk;
1155
1156 if t > 1 {
1162 let x_f32 = x.to_dtype(crate::DType::F32)?.contiguous()?;
1163 let ids_u32 = ids.to_dtype(crate::DType::U32)?.contiguous()?;
1164 let (xs, _) = x_f32.storage_and_layout();
1165 let xc = match &*xs {
1166 Storage::Cuda(c) => c,
1167 _ => crate::bail!("cuda i-quant MoE: x not on cuda after contiguous()"),
1168 };
1169 let (ids_s, _) = ids_u32.storage_and_layout();
1170 let idc = match &*ids_s {
1171 Storage::Cuda(c) => c,
1172 _ => crate::bail!("cuda i-quant MoE: ids not on cuda"),
1173 };
1174 if let Some((st, sh)) = s.moe_iquant_qmmq(
1175 self.shape(),
1176 xc.as_cuda_slice::<f32>()?,
1177 x.shape(),
1178 &idc.as_cuda_slice::<u32>()?.slice(0..),
1179 ids.shape(),
1180 )? {
1181 return crate::tensor::from_storage(
1182 Storage::Cuda(st),
1183 sh,
1184 crate::op::BackpropOp::none(),
1185 false,
1186 )
1187 .to_dtype(out_dtype);
1188 }
1189 }
1190
1191 let sdim = x.dim(1)?; let x_exp = if sdim == topk {
1196 x.clone()
1197 } else {
1198 x.broadcast_as((t, topk, k))?
1199 };
1200 let x_flat = x_exp
1201 .reshape((nrows, k))?
1202 .to_dtype(crate::DType::F32)?
1203 .contiguous()?;
1204 let ids_flat = ids
1205 .reshape((nrows,))?
1206 .to_dtype(crate::DType::U32)?
1207 .contiguous()?;
1208 let (xstore, _) = x_flat.storage_and_layout();
1209 let xc = match &*xstore {
1210 Storage::Cuda(c) => c,
1211 _ => crate::bail!("cuda i-quant MoE: x not on cuda after contiguous()"),
1212 };
1213 let (idstore, _) = ids_flat.storage_and_layout();
1214 let idc = match &*idstore {
1215 Storage::Cuda(c) => c,
1216 _ => crate::bail!("cuda i-quant MoE: ids not on cuda"),
1217 };
1218 let y = s.moe_iquant_dp4a(
1219 &xc.as_cuda_slice::<f32>()?.slice(0..),
1220 &idc.as_cuda_slice::<u32>()?.slice(0..),
1221 nrows,
1222 n,
1223 k,
1224 )?;
1225 let out = crate::tensor::from_storage(
1226 Storage::Cuda(y),
1227 (nrows, n),
1228 crate::op::BackpropOp::none(),
1229 false,
1230 );
1231 out.reshape((t, topk, n))?.to_dtype(out_dtype)
1232 }
1233 #[cfg(feature = "vulkan")]
1239 QStorage::Vulkan(_, vk_dev) if vk_moe_kernel(self.storage.dtype()).is_some() => {
1240 let out_dtype = x.dtype();
1241 let (e_cnt, n, k) = self.shape().dims3()?;
1242 let (t, topk) = ids.dims2()?;
1243 let s = x.dim(1)?; let x_exp = if s == topk {
1245 x.clone()
1246 } else {
1247 x.broadcast_as((t, topk, k))?
1248 };
1249 let nrows = t * topk;
1251 let x_flat = x_exp
1252 .reshape((nrows, k))?
1253 .to_dtype(crate::DType::F32)?
1254 .contiguous()?;
1255 let dt = self.storage.dtype();
1262 let ids_u32 = ids.reshape((nrows,))?.to_dtype(crate::DType::U32)?.contiguous()?;
1263 let y = {
1264 let (store, _) = x_flat.storage_and_layout();
1265 let xv = match &*store {
1266 Storage::Vulkan(v) => v,
1267 _ => crate::bail!("vulkan MoE: x not on vulkan after contiguous()"),
1268 };
1269 let (ids_store, _) = ids_u32.storage_and_layout();
1270 let ids_v = match &*ids_store {
1271 Storage::Vulkan(v) => v,
1272 _ => crate::bail!("vulkan MoE: ids not on vulkan after contiguous()"),
1273 };
1274 match vk_moe_blk_dp4a_kernel(dt, n, k).filter(|_| vk_dev.has_int_dot8()) {
1280 Some((blk, with_xsum)) => {
1281 let bank = self.vulkan_moe_bank_split(vk_dev, e_cnt, n, k)?;
1282 vk_dev.moe_matvec_blk_dp4a_gpu(blk, with_xsum, bank.as_ref(), xv, ids_v, nrows, n, k)?
1283 }
1284 None => match vk_moe_blk_kernel(dt, n, k) {
1285 Some(blk) => {
1286 let bank = self.vulkan_moe_bank_split(vk_dev, e_cnt, n, k)?;
1287 vk_dev.moe_matvec_blk_gpu(blk, bank.as_ref(), xv, ids_v, nrows, n, k)?
1288 }
1289 None => {
1290 let kernel = vk_moe_kernel(dt).unwrap();
1292 let wbank = self.vulkan_moe_bank(vk_dev, e_cnt, n, k)?;
1293 vk_dev.moe_matvec_gpu(kernel, wbank.as_ref(), xv, ids_v, nrows, n, k)?
1294 }
1295 },
1296 }
1297 };
1298 let out = crate::tensor::from_storage(
1299 Storage::Vulkan(y),
1300 (nrows, n),
1301 crate::op::BackpropOp::none(),
1302 false,
1303 );
1304 out.reshape((t, topk, n))?.to_dtype(out_dtype)
1305 }
1306 #[cfg(feature = "wgpu")]
1310 QStorage::Wgpu(_, wgpu_dev) if wgpu_moe_kernel(self.storage.dtype()).is_some() => {
1311 let out_dtype = x.dtype();
1312 let (e_cnt, n, k) = self.shape().dims3()?;
1313 let (t, topk) = ids.dims2()?;
1314 let s = x.dim(1)?; let x_exp = if s == topk {
1316 x.clone()
1317 } else {
1318 x.broadcast_as((t, topk, k))?
1319 };
1320 let nrows = t * topk;
1321 let x_flat = x_exp
1322 .reshape((nrows, k))?
1323 .to_dtype(crate::DType::F32)?
1324 .contiguous()?;
1325 let ids_vec = ids
1326 .reshape((nrows,))?
1327 .to_dtype(crate::DType::U32)?
1328 .to_vec1::<u32>()?;
1329 if let Some(&bad) = ids_vec.iter().find(|&&e| e as usize >= e_cnt) {
1330 crate::bail!("indexed_moe_forward: expert id {bad} >= num_experts {e_cnt}");
1331 }
1332 let kernel = wgpu_moe_kernel(self.storage.dtype()).unwrap();
1334 let wbank = self.wgpu_moe_bank(wgpu_dev)?;
1336 let ids_buf = wgpu_dev.upload_ids(&ids_vec)?;
1337 let y = {
1338 let (store, _) = x_flat.storage_and_layout();
1339 let xv = match &*store {
1340 Storage::Wgpu(v) => v,
1341 _ => crate::bail!("wgpu MoE: x not on wgpu after contiguous()"),
1342 };
1343 wgpu_dev.moe_matvec_gpu(kernel, wbank.as_ref(), xv, &ids_buf, nrows, n, k)?
1344 };
1345 let out = crate::tensor::from_storage(
1346 Storage::Wgpu(y),
1347 (nrows, n),
1348 crate::op::BackpropOp::none(),
1349 false,
1350 );
1351 out.reshape((t, topk, n))?.to_dtype(out_dtype)
1352 }
1353 #[cfg(feature = "rocm")]
1358 QStorage::Rocm(_, rocm_dev)
1359 if crate::RocmQuantType::from_ggml(self.storage.dtype()).is_some() =>
1360 {
1361 let qt = crate::RocmQuantType::from_ggml(self.storage.dtype()).unwrap();
1362 let out_dtype = x.dtype();
1363 let (_e_cnt, n, k) = self.shape().dims3()?;
1366 let (t, topk) = ids.dims2()?;
1367 let s = x.dim(1)?; let x_exp = if s == topk {
1369 x.clone()
1370 } else {
1371 x.broadcast_as((t, topk, k))?
1372 };
1373 let nrows = t * topk;
1374 let use_qmmq = t > 1 && qt.qmmq_capable();
1380 let x_flat = match x_exp.dtype() {
1381 DType::F16 | DType::F32 if use_qmmq => {
1385 x_exp.reshape((nrows, k))?.contiguous()?
1386 }
1387 _ if use_qmmq => x_exp
1388 .reshape((nrows, k))?
1389 .to_dtype(DType::F16)?
1390 .contiguous()?,
1391 DType::BF16 | DType::F16 => x_exp.reshape((nrows, k))?.contiguous()?,
1392 DType::F32 if qt.dp4a_active() => x_exp.reshape((nrows, k))?.contiguous()?,
1396 _ => x_exp
1397 .reshape((nrows, k))?
1398 .to_dtype(DType::F16)?
1399 .contiguous()?,
1400 };
1401 let wbank = self.rocm_moe_bank(rocm_dev)?;
1402 let ids_u32 = ids
1409 .reshape((nrows,))?
1410 .to_dtype(crate::DType::U32)?
1411 .contiguous()?;
1412 let (store, _) = x_flat.storage_and_layout();
1413 let xr = match &*store {
1414 crate::Storage::Rocm(r) => r,
1415 _ => crate::bail!("rocm MoE: x not on rocm after contiguous()"),
1416 };
1417 let (idstore, _) = ids_u32.storage_and_layout();
1418 let idr = match &*idstore {
1419 crate::Storage::Rocm(r) => r,
1420 _ => crate::bail!("rocm MoE: ids not on rocm"),
1421 };
1422 let y = if use_qmmq {
1423 rocm_dev.moe_qmmq_quant(qt, wbank.as_ref(), xr, idr, nrows, n, k)?
1424 } else {
1425 rocm_dev.moe_matvec_quant(qt, wbank.as_ref(), xr, idr, nrows, n, k)?
1426 };
1427 let out = crate::tensor::from_storage(
1428 crate::Storage::Rocm(y),
1429 (nrows, n),
1430 crate::op::BackpropOp::none(),
1431 false,
1432 );
1433 out.reshape((t, topk, n))?.to_dtype(out_dtype)
1434 }
1435 #[cfg(feature = "metal")]
1443 QStorage::Metal(s)
1444 if matches!(&*x.storage(), Storage::Metal(_))
1445 && matches!(&*ids.storage(), Storage::Metal(_)) =>
1446 {
1447 let out_dtype = x.dtype();
1448 let x = x.contiguous()?;
1449 let (xs_guard, x_l) = x.storage_and_layout();
1450 let (ids_guard, ids_l) = ids.storage_and_layout();
1451 let (Storage::Metal(x_storage), Storage::Metal(ids_storage)) =
1452 (&*xs_guard, &*ids_guard)
1453 else {
1454 unreachable!("metal MoE arm is guarded on Metal x/ids storage");
1455 };
1456 let (storage, out_shape) =
1457 s.indexed_moe_forward(self.shape(), x_storage, x_l, ids_storage, ids_l)?;
1458 let out = crate::tensor::from_storage(
1459 Storage::Metal(storage),
1460 out_shape,
1461 crate::op::BackpropOp::none(),
1462 false,
1463 );
1464 out.to_dtype(out_dtype)
1465 }
1466 QStorage::Stream(bank) => {
1470 let (e_cnt, n, k) = self.shape().dims3()?;
1471 let dtype = bank.dtype();
1472 moe_grouped_per_expert(x, ids, n, k, |eid, device| {
1473 if eid as usize >= e_cnt {
1474 crate::bail!("indexed_moe_forward: expert id {eid} >= num_experts {e_cnt}");
1475 }
1476 let bytes = bank.fetch(eid)?;
1477 QStorage::from_data(std::borrow::Cow::Borrowed(&bytes), device, dtype)
1478 })
1479 }
1480 _ => {
1481 let (e_cnt, n, k) = self.shape().dims3()?;
1487 let dtype = self.storage.dtype();
1488 let all_bytes = self.data()?;
1489 let expert_bytes = all_bytes.len() / e_cnt;
1490 moe_grouped_per_expert(x, ids, n, k, |eid, device| {
1491 let off = eid as usize * expert_bytes;
1492 QStorage::from_data(
1493 std::borrow::Cow::Borrowed(&all_bytes[off..off + expert_bytes]),
1494 device,
1495 dtype,
1496 )
1497 })
1498 }
1499 }
1500 }
1501
1502 pub fn device_ptr(&self) -> Result<*const u8> {
1503 match &self.storage {
1504 QStorage::Cuda(storage) => storage.device_ptr(),
1505 #[cfg(feature = "rocm")]
1506 QStorage::Rocm(..) => crate::bail!("not implemented"),
1507 #[cfg(feature = "vulkan")]
1508 QStorage::Vulkan(..) => crate::bail!("not implemented"),
1509 #[cfg(feature = "wgpu")]
1510 QStorage::Wgpu(..) => crate::bail!("not implemented"),
1511 QStorage::Metal(_) | QStorage::Cpu(_) | QStorage::Stream(_) => {
1512 crate::bail!("not implemented");
1513 }
1514 }
1515 }
1516
1517 #[cfg(feature = "cuda")]
1518 pub fn device_ptr_with_guard<'a>(
1519 &'a self,
1520 stream: &'a crate::cuda_backend::cudarc::driver::CudaStream,
1521 ) -> Result<(
1522 *const u8,
1523 crate::cuda_backend::cudarc::driver::SyncOnDrop<'a>,
1524 )> {
1525 self.storage.device_ptr_with_guard(stream)
1526 }
1527}
1528
1529#[derive(Clone, Debug)]
1530pub enum QMatMul {
1531 QTensor(std::sync::Arc<QTensor>),
1532 Tensor(Tensor),
1533 TensorF16(Tensor),
1534 #[cfg(feature = "vulkan")]
1540 VulkanQuant {
1541 qtensor: std::sync::Arc<QTensor>,
1542 wq: std::sync::Arc<crate::VulkanStorage>,
1543 dtype: GgmlDType,
1544 n: usize,
1545 k: usize,
1546 },
1547 #[cfg(feature = "wgpu")]
1551 WgpuQuant {
1552 qtensor: std::sync::Arc<QTensor>,
1553 wq: std::sync::Arc<crate::WgpuStorage>,
1554 dtype: GgmlDType,
1555 n: usize,
1556 k: usize,
1557 },
1558 #[cfg(feature = "rocm")]
1564 RocmQuant {
1565 qtensor: std::sync::Arc<QTensor>,
1566 wq: std::sync::Arc<crate::RocmStorage>,
1567 dtype: GgmlDType,
1568 n: usize,
1569 k: usize,
1570 },
1571}
1572
1573#[cfg(any(feature = "rocm", feature = "vulkan", feature = "wgpu"))]
1583fn cache_or_upload<S>(
1584 slot: &std::sync::OnceLock<std::sync::Arc<S>>,
1585 bank: &[u8],
1586 upload: impl FnOnce(&[u8]) -> Result<S>,
1587) -> Result<std::sync::Arc<S>> {
1588 if let Some(w) = slot.get() {
1589 return Ok(w.clone());
1590 }
1591 let w = std::sync::Arc::new(upload(bank)?);
1592 Ok(slot.get_or_init(|| w).clone())
1595}
1596
1597#[cfg(feature = "vulkan")]
1603fn vk_moe_kernel(dt: GgmlDType) -> Option<&'static str> {
1604 match dt {
1605 GgmlDType::Q4_0 => Some("moe_matvec_q4_0"),
1606 GgmlDType::Q8_0 => Some("moe_matvec_q8_0"),
1607 GgmlDType::Q4K => Some("moe_matvec_q4k"),
1608 GgmlDType::Q6K => Some("moe_matvec_q6k"),
1609 _ => None,
1610 }
1611}
1612
1613#[cfg(feature = "vulkan")]
1619fn vk_moe_blk_kernel(dt: GgmlDType, n: usize, k: usize) -> Option<&'static str> {
1620 if std::env::var_os("VK_MOE_PACKED").is_some() {
1623 return None;
1624 }
1625 match (dt, n, k) {
1626 (GgmlDType::Q4K, 768, 2048) => Some("moe_matvec_q4k_blk_gu"),
1627 (GgmlDType::Q4K, 2048, 768) => Some("moe_matvec_q4k_blk_dn"),
1628 (GgmlDType::Q6K, 2048, 768) => Some("moe_matvec_q6k_blk_dn"),
1629 _ => None,
1630 }
1631}
1632
1633fn vk_moe_blk_dp4a_kernel(dt: GgmlDType, n: usize, k: usize) -> Option<(&'static str, bool)> {
1639 if std::env::var_os("VK_MOE_PACKED").is_some() || std::env::var_os("VK_MOE_DP4A_OFF").is_some() {
1640 return None;
1641 }
1642 match (dt, n, k) {
1643 (GgmlDType::Q4K, 768, 2048) => Some(("moe_matvec_q4k_dp4a_blk_gu", true)),
1644 (GgmlDType::Q4K, 2048, 768) => Some(("moe_matvec_q4k_dp4a_blk_dn", true)),
1645 (GgmlDType::Q6K, 2048, 768) => Some(("moe_matvec_q6k_dp4a_blk_dn", false)),
1646 _ => None,
1647 }
1648}
1649
1650#[cfg(feature = "wgpu")]
1652fn wgpu_moe_kernel(dt: GgmlDType) -> Option<&'static str> {
1653 match dt {
1654 GgmlDType::Q4_0 => Some("moe_matvec_q4_0"),
1655 GgmlDType::Q8_0 => Some("moe_matvec_q8_0"),
1656 GgmlDType::Q4K => Some("moe_matvec_q4k"),
1657 _ => None,
1658 }
1659}
1660
1661fn moe_grouped_per_expert(
1666 x: &Tensor,
1667 ids: &Tensor,
1668 n: usize,
1669 k: usize,
1670 mut make_storage: impl FnMut(u32, &Device) -> Result<QStorage>,
1671) -> Result<Tensor> {
1672 use crate::Module; use std::collections::HashMap;
1674 use std::sync::Arc;
1675 let device = x.device();
1676 let out_dtype = x.dtype();
1677 let (t, topk) = ids.dims2()?;
1678 let s = x.dim(1)?; let x_exp = if s == topk {
1680 x.clone()
1681 } else {
1682 x.broadcast_as((t, topk, k))?
1683 };
1684 let x_flat = x_exp
1685 .reshape((t * topk, k))?
1686 .to_dtype(DType::F32)?
1687 .contiguous()?;
1688 let ids_flat = ids.reshape((t * topk,))?.to_dtype(DType::U32)?;
1689 let ids_vec = ids_flat.to_vec1::<u32>()?;
1690 let mut groups: HashMap<u32, Vec<u32>> = HashMap::new();
1691 for (slot, eid) in ids_vec.iter().enumerate() {
1692 groups.entry(*eid).or_default().push(slot as u32);
1693 }
1694 let mut out_flat = Tensor::zeros((t * topk, n), DType::F32, device)?;
1695 for (eid, slots) in groups.into_iter() {
1696 let qs = make_storage(eid, device)?;
1697 let shape: crate::Shape = (n, k).into();
1698 let w_e = QTensor::make(qs, shape);
1699 let qm = QMatMul::from_arc(Arc::new(w_e))?;
1700 let m = slots.len();
1701 let idx = Tensor::from_vec(slots, (m,), device)?;
1702 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)?;
1705 }
1706 out_flat.reshape((t, topk, n))?.to_dtype(out_dtype)
1707}
1708
1709#[cfg_attr(not(feature = "rocm"), allow(unused_variables))]
1715pub fn moe_combine(ys: &Tensor, scores: &Tensor) -> Result<Tensor> {
1716 let (t, topk, n) = ys.dims3()?;
1717 #[cfg(feature = "rocm")]
1718 if let Device::Rocm(dev) = ys.device() {
1719 let ys_c = ys.contiguous()?;
1720 let scores_c = scores.to_dtype(DType::F32)?.contiguous()?;
1721 let (ys_store, _) = ys_c.storage_and_layout();
1722 let yr = match &*ys_store {
1723 Storage::Rocm(r) => r,
1724 _ => crate::bail!("moe_combine: ys not on rocm after contiguous()"),
1725 };
1726 let (sc_store, _) = scores_c.storage_and_layout();
1727 let sr = match &*sc_store {
1728 Storage::Rocm(r) => r,
1729 _ => crate::bail!("moe_combine: scores not on rocm after contiguous()"),
1730 };
1731 let out = dev.moe_combine(yr, sr, t, topk, n)?;
1732 return Ok(crate::tensor::from_storage(
1733 Storage::Rocm(out),
1734 (t, n),
1735 crate::op::BackpropOp::none(),
1736 false,
1737 ));
1738 }
1739 let out_dtype = ys.dtype();
1744 ys.to_dtype(DType::F32)?
1745 .broadcast_mul(&scores.to_dtype(DType::F32)?.unsqueeze(D::Minus1)?)?
1746 .sum(D::Minus2)?
1747 .to_dtype(out_dtype)
1748}
1749
1750#[cfg_attr(not(feature = "rocm"), allow(unused_variables))]
1755pub fn moe_route(logits: &Tensor, topk: usize, norm: bool) -> Result<(Tensor, Tensor)> {
1756 let (ntok, n_experts) = logits.dims2()?;
1757 #[cfg(feature = "rocm")]
1758 if let Device::Rocm(dev) = logits.device() {
1759 let logits_c = logits.to_dtype(DType::F32)?.contiguous()?;
1760 let (lg_store, _) = logits_c.storage_and_layout();
1761 let lr = match &*lg_store {
1762 Storage::Rocm(r) => r,
1763 _ => crate::bail!("moe_route: logits not on rocm after contiguous()"),
1764 };
1765 let (ids, w) = dev.moe_route(lr, ntok, n_experts, topk, norm)?;
1766 let ids_t = crate::tensor::from_storage(
1767 Storage::Rocm(ids),
1768 (ntok, topk),
1769 crate::op::BackpropOp::none(),
1770 false,
1771 );
1772 let w_t = crate::tensor::from_storage(
1773 Storage::Rocm(w),
1774 (ntok, topk),
1775 crate::op::BackpropOp::none(),
1776 false,
1777 );
1778 return Ok((ids_t, w_t));
1779 }
1780 #[cfg(feature = "cuda")]
1781 if let Device::Cuda(cdev) = logits.device() {
1782 if n_experts <= 256 && topk <= 32 {
1783 let logits_c = logits.to_dtype(DType::F32)?.contiguous()?;
1784 let (lg_store, _) = logits_c.storage_and_layout();
1785 let lr = match &*lg_store {
1786 Storage::Cuda(c) => c,
1787 _ => crate::bail!("moe_route: logits not on cuda after contiguous()"),
1788 };
1789 let lview = lr.as_cuda_slice::<f32>()?.slice(0..);
1790 let (ids, w) = cuda::moe_route(&lview, ntok, n_experts, topk, norm, cdev)?;
1791 let ids_t = crate::tensor::from_storage(
1792 Storage::Cuda(ids),
1793 (ntok, topk),
1794 crate::op::BackpropOp::none(),
1795 false,
1796 );
1797 let w_t = crate::tensor::from_storage(
1798 Storage::Cuda(w),
1799 (ntok, topk),
1800 crate::op::BackpropOp::none(),
1801 false,
1802 );
1803 return Ok((ids_t, w_t));
1804 }
1805 }
1806 #[cfg(feature = "vulkan")]
1810 if let Device::Vulkan(vdev) = logits.device() {
1811 if norm && n_experts == 128 && topk == 8 {
1812 let logits_c = logits.to_dtype(DType::F32)?.contiguous()?;
1813 let (lg_store, _) = logits_c.storage_and_layout();
1814 let lv = match &*lg_store {
1815 Storage::Vulkan(v) => v,
1816 _ => crate::bail!("moe_route: logits not on vulkan after contiguous()"),
1817 };
1818 let (ids, w) = vdev.moe_route_vk(lv, ntok, n_experts, topk)?;
1819 let ids_t = crate::tensor::from_storage(
1820 Storage::Vulkan(ids),
1821 (ntok, topk),
1822 crate::op::BackpropOp::none(),
1823 false,
1824 );
1825 let w_t = crate::tensor::from_storage(
1826 Storage::Vulkan(w),
1827 (ntok, topk),
1828 crate::op::BackpropOp::none(),
1829 false,
1830 );
1831 return Ok((ids_t, w_t));
1832 }
1833 }
1834 let lf = logits.to_dtype(DType::F32)?;
1835 let mx = lf.max_keepdim(D::Minus1)?;
1836 let e = lf.broadcast_sub(&mx)?.exp()?;
1837 let z = e.sum_keepdim(D::Minus1)?;
1838 let p = e.broadcast_div(&z)?;
1839 let (sv, si) = p.sort_last_dim(false)?;
1840 let ids = si.narrow(D::Minus1, 0, topk)?.contiguous()?;
1841 let mut w = sv.narrow(D::Minus1, 0, topk)?.contiguous()?;
1842 if norm {
1843 w = w.broadcast_div(&w.sum_keepdim(D::Minus1)?)?;
1844 }
1845 Ok((ids, w))
1846}
1847
1848pub fn moe_gate_up(
1856 x: &Tensor,
1857 ids: &Tensor,
1858 gate: &QMatMul,
1859 up: &QMatMul,
1860) -> Result<(Tensor, Tensor)> {
1861 #[cfg(feature = "rocm")]
1862 {
1863 if let (QMatMul::QTensor(gq), QMatMul::QTensor(uq)) = (gate, up) {
1864 if let (QStorage::Rocm(_, dev), QStorage::Rocm(..)) = (&gq.storage, &uq.storage) {
1865 let dt = gq.storage.dtype();
1866 if dt == uq.storage.dtype() {
1867 if let Some(qt) = crate::RocmQuantType::from_ggml(dt) {
1868 let (_e, n, k) = gq.shape().dims3()?;
1869 let (t, topk) = ids.dims2()?;
1870 let use_qmmq = t > 1 && qt.qmmq_capable();
1871 if x.dim(1)? == 1 && !use_qmmq {
1872 let nrows = t * topk;
1873 let x_exp = x.broadcast_as((t, topk, k))?;
1874 let x_flat = match x_exp.dtype() {
1875 DType::BF16 | DType::F16 => {
1876 x_exp.reshape((nrows, k))?.contiguous()?
1877 }
1878 DType::F32 if qt.dp4a_active() => {
1879 x_exp.reshape((nrows, k))?.contiguous()?
1880 }
1881 _ => x_exp
1882 .reshape((nrows, k))?
1883 .to_dtype(DType::F16)?
1884 .contiguous()?,
1885 };
1886 let out_dtype = x.dtype();
1887 let ids_u32 =
1888 ids.reshape((nrows,))?.to_dtype(DType::U32)?.contiguous()?;
1889 let gwb = gq.rocm_moe_bank(dev)?;
1890 let uwb = uq.rocm_moe_bank(dev)?;
1891 let (xstore, _) = x_flat.storage_and_layout();
1892 let xr = match &*xstore {
1893 Storage::Rocm(r) => r,
1894 _ => crate::bail!("moe_gate_up: x not on rocm after contiguous()"),
1895 };
1896 let (idstore, _) = ids_u32.storage_and_layout();
1897 let idr = match &*idstore {
1898 Storage::Rocm(r) => r,
1899 _ => crate::bail!("moe_gate_up: ids not on rocm"),
1900 };
1901 let (gy, uy) = dev.moe_matvec_pair(
1902 qt,
1903 gwb.as_ref(),
1904 uwb.as_ref(),
1905 xr,
1906 idr,
1907 nrows,
1908 n,
1909 k,
1910 )?;
1911 let g = crate::tensor::from_storage(
1912 Storage::Rocm(gy),
1913 (nrows, n),
1914 crate::op::BackpropOp::none(),
1915 false,
1916 )
1917 .reshape((t, topk, n))?
1918 .to_dtype(out_dtype)?;
1919 let u = crate::tensor::from_storage(
1920 Storage::Rocm(uy),
1921 (nrows, n),
1922 crate::op::BackpropOp::none(),
1923 false,
1924 )
1925 .reshape((t, topk, n))?
1926 .to_dtype(out_dtype)?;
1927 return Ok((g, u));
1928 }
1929 }
1930 }
1931 }
1932 }
1933 }
1934 #[cfg(feature = "vulkan")]
1941 if std::env::var_os("VK_MOE_GU_FUSE_OFF").is_none() {
1942 if let (QMatMul::QTensor(gq), QMatMul::QTensor(uq)) = (gate, up) {
1943 if let (QStorage::Vulkan(_, dev), QStorage::Vulkan(..)) = (&gq.storage, &uq.storage) {
1944 let dt = gq.storage.dtype();
1945 if dt == uq.storage.dtype() && dev.has_int_dot8() {
1946 let (e_cnt, n, k) = gq.shape().dims3()?;
1947 if uq.shape().dims3()? == (e_cnt, n, k) && x.dim(1)? == 1 {
1949 if let Some((blk, with_xsum)) = vk_moe_blk_dp4a_kernel(dt, n, k) {
1950 let (t, topk) = ids.dims2()?;
1951 let nrows = t * topk;
1952 let x_flat = x
1953 .broadcast_as((t, topk, k))?
1954 .reshape((nrows, k))?
1955 .to_dtype(DType::F32)?
1956 .contiguous()?;
1957 let ids_u32 = ids.reshape((nrows,))?.to_dtype(DType::U32)?.contiguous()?;
1958 let (xstore, _) = x_flat.storage_and_layout();
1959 let xv = match &*xstore {
1960 Storage::Vulkan(v) => v,
1961 _ => crate::bail!("moe_gate_up: x not on vulkan after contiguous()"),
1962 };
1963 let (idstore, _) = ids_u32.storage_and_layout();
1964 let idv = match &*idstore {
1965 Storage::Vulkan(v) => v,
1966 _ => crate::bail!("moe_gate_up: ids not on vulkan"),
1967 };
1968 let (xq, xs, xsum) = dev.quantize_act_q8(xv, nrows, k)?;
1970 let gbank = gq.vulkan_moe_bank_split(dev, e_cnt, n, k)?;
1971 let ubank = uq.vulkan_moe_bank_split(dev, e_cnt, n, k)?;
1972 let out_dtype = x.dtype();
1973 let gy = dev.moe_matvec_blk_dp4a_pre_gpu(
1974 blk, with_xsum, gbank.as_ref(), &xq, &xs, &xsum, idv, nrows, n,
1975 )?;
1976 let uy = dev.moe_matvec_blk_dp4a_pre_gpu(
1977 blk, with_xsum, ubank.as_ref(), &xq, &xs, &xsum, idv, nrows, n,
1978 )?;
1979 let shape = |o| -> Result<Tensor> {
1980 crate::tensor::from_storage(
1981 Storage::Vulkan(o),
1982 (nrows, n),
1983 crate::op::BackpropOp::none(),
1984 false,
1985 )
1986 .reshape((t, topk, n))?
1987 .to_dtype(out_dtype)
1988 };
1989 return Ok((shape(gy)?, shape(uy)?));
1990 }
1991 }
1992 }
1993 }
1994 }
1995 }
1996 Ok((
1997 gate.indexed_moe_forward(x, ids)?,
1998 up.indexed_moe_forward(x, ids)?,
1999 ))
2000}
2001
2002impl QMatMul {
2003 pub fn from_arc(qtensor: std::sync::Arc<QTensor>) -> Result<Self> {
2004 #[cfg(feature = "vulkan")]
2010 {
2011 let dt = qtensor.dtype();
2012 let native_vk = matches!(
2013 dt,
2014 GgmlDType::Q4_0
2015 | GgmlDType::Q8_0
2016 | GgmlDType::Q4K
2017 | GgmlDType::Q5K
2018 | GgmlDType::Q6K
2019 | GgmlDType::Q2K
2020 | GgmlDType::Q3K
2021 | GgmlDType::IQ4_XS
2022 | GgmlDType::IQ4_NL
2023 | GgmlDType::TQ2_0
2024 | GgmlDType::IQ2_XXS
2025 | GgmlDType::IQ2_S
2026 | GgmlDType::IQ3_XXS
2027 | GgmlDType::IQ3_S
2028 | GgmlDType::IQ1_S
2029 | GgmlDType::IQ1_M
2030 | GgmlDType::IQ2_XS
2031 );
2032 if native_vk {
2033 if let Device::Vulkan(d) = qtensor.device() {
2034 if let Ok((n, k)) = qtensor.shape().dims2() {
2035 let blk = dt.block_size();
2036 if k % blk == 0 {
2037 let bytes = qtensor.data()?;
2038 let wq = match dt {
2045 GgmlDType::Q6K => d.quantize_q6k(&bytes, n, k)?,
2046 GgmlDType::Q3K => d.quantize_q3k(&bytes, n, k)?,
2047 GgmlDType::Q8_0 => d.quantize_q8_blocks(&bytes, n, k)?,
2048 GgmlDType::IQ2_XXS => d.quantize_iq2xxs(&bytes, n, k)?,
2049 GgmlDType::IQ2_XS => d.quantize_iq2xs(&bytes, n, k)?,
2050 GgmlDType::IQ1_M => d.quantize_iq1m(&bytes, n, k)?,
2051 GgmlDType::IQ1_S => d.quantize_iq1s(&bytes, n, k)?,
2052 GgmlDType::IQ3_S => d.quantize_iq3s(&bytes, n, k)?,
2053 GgmlDType::IQ3_XXS => d.quantize_iq3xxs(&bytes, n, k)?,
2054 GgmlDType::IQ2_S => d.quantize_iq2s(&bytes, n, k)?,
2055 _ => d.upload_qweight(&bytes)?,
2056 };
2057 return Ok(Self::VulkanQuant {
2058 qtensor,
2059 wq: std::sync::Arc::new(wq),
2060 dtype: dt,
2061 n,
2062 k,
2063 });
2064 }
2065 }
2066 }
2067 }
2068 }
2069 #[cfg(feature = "wgpu")]
2072 {
2073 let dt = qtensor.dtype();
2074 let native_wgpu = matches!(dt, GgmlDType::Q4_0 | GgmlDType::Q8_0 | GgmlDType::Q4K);
2075 if native_wgpu {
2076 if let Device::Wgpu(d) = qtensor.device() {
2077 if let Ok((n, k)) = qtensor.shape().dims2() {
2078 let blk = dt.block_size();
2079 if k % blk == 0 {
2080 let bytes = qtensor.data()?;
2081 let wq = d.upload_qweight(&bytes)?;
2082 return Ok(Self::WgpuQuant {
2083 qtensor,
2084 wq: std::sync::Arc::new(wq),
2085 dtype: dt,
2086 n,
2087 k,
2088 });
2089 }
2090 }
2091 }
2092 }
2093 }
2094 #[cfg(feature = "rocm")]
2100 {
2101 let dt = qtensor.dtype();
2102 if let Some(qt) = crate::RocmQuantType::from_ggml(dt) {
2107 if let Device::Rocm(d) = qtensor.device() {
2108 if let Ok((n, k)) = qtensor.shape().dims2() {
2109 let blk_ok = k % qt.block_elems() == 0;
2110 if blk_ok {
2111 use crate::backend::BackendDevice;
2112 let bytes = qtensor.data()?;
2113 let wq = d.storage_from_slice(bytes.as_ref())?;
2114 return Ok(Self::RocmQuant {
2115 qtensor,
2116 wq: std::sync::Arc::new(wq),
2117 dtype: dt,
2118 n,
2119 k,
2120 });
2121 }
2122 }
2123 }
2124 }
2125 }
2126 #[cfg(feature = "rocm")]
2132 {
2133 if qtensor.device().is_rocm()
2134 && qtensor.shape().dims().len() == 3
2135 && crate::RocmQuantType::from_ggml(qtensor.dtype()).is_some()
2136 {
2137 return Ok(Self::QTensor(qtensor));
2138 }
2139 }
2140 #[cfg(feature = "vulkan")]
2146 {
2147 if qtensor.device().is_vulkan()
2148 && qtensor.shape().dims().len() == 3
2149 && vk_moe_kernel(qtensor.dtype()).is_some()
2150 {
2151 return Ok(Self::QTensor(qtensor));
2152 }
2153 }
2154 #[cfg(feature = "wgpu")]
2155 {
2156 if qtensor.device().is_wgpu()
2157 && qtensor.shape().dims().len() == 3
2158 && wgpu_moe_kernel(qtensor.dtype()).is_some()
2159 {
2160 return Ok(Self::QTensor(qtensor));
2161 }
2162 }
2163 let dequantize = match qtensor.dtype() {
2164 GgmlDType::F32 | GgmlDType::F16 | GgmlDType::BF16 | GgmlDType::I32 => true,
2165 _ => {
2168 qtensor.device().is_vulkan()
2169 || qtensor.device().is_wgpu()
2170 || qtensor.device().is_rocm()
2171 }
2172 };
2173 let t = if dequantize {
2174 if qtensor.device().is_rocm() {
2178 Self::TensorF16(qtensor.dequantize_f16(&qtensor.device())?)
2179 } else {
2180 Self::Tensor(qtensor.dequantize(&qtensor.device())?)
2181 }
2182 } else {
2183 Self::QTensor(qtensor)
2184 };
2185 Ok(t)
2186 }
2187
2188 pub fn from_qtensor(qtensor: QTensor) -> Result<Self> {
2189 Self::from_arc(std::sync::Arc::new(qtensor))
2190 }
2191
2192 pub fn dequantize_f16(&self) -> Result<Tensor> {
2193 match self {
2194 Self::QTensor(t) => t.dequantize_f16(&t.device()),
2195 Self::Tensor(t) => t.to_dtype(DType::F16),
2196 Self::TensorF16(t) => Ok(t.clone()),
2197 #[cfg(feature = "rocm")]
2198 Self::RocmQuant { qtensor, .. } => qtensor.dequantize_f16(&qtensor.device()),
2199 #[cfg(feature = "vulkan")]
2200 Self::VulkanQuant { qtensor, .. } => qtensor.dequantize_f16(&qtensor.device()),
2201 #[cfg(feature = "wgpu")]
2202 Self::WgpuQuant { qtensor, .. } => qtensor.dequantize_f16(&qtensor.device()),
2203 }
2204 }
2205
2206 pub fn forward_via_f16(&self, xs: &Tensor) -> Result<Tensor> {
2207 let w = self.dequantize_f16()?;
2208 let in_dtype = xs.dtype();
2209 let w = match *xs.dims() {
2210 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2211 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2212 _ => w.t()?,
2213 };
2214 xs.to_dtype(DType::F16)?.matmul(&w)?.to_dtype(in_dtype)
2215 }
2216
2217 pub fn indexed_moe_forward(&self, x: &Tensor, ids: &Tensor) -> Result<Tensor> {
2218 match self {
2219 Self::QTensor(t) => t.indexed_moe_forward(x, ids),
2220 #[cfg(feature = "rocm")]
2225 Self::RocmQuant {
2226 qtensor, wq, dtype, ..
2227 } if crate::RocmQuantType::from_ggml(*dtype).is_some() => {
2228 let qt = crate::RocmQuantType::from_ggml(*dtype).unwrap();
2229 let wbank = wq.as_ref();
2230 let (_e_cnt, n, k) = qtensor.shape().dims3()?;
2232 let (t, topk) = ids.dims2()?;
2233 let s = x.dim(1)?; let x_exp = if s == topk {
2235 x.clone()
2236 } else {
2237 x.broadcast_as((t, topk, k))?
2238 };
2239 let nrows = t * topk;
2240 let use_qmmq = t > 1 && qt.qmmq_capable();
2246 let x_flat = match x_exp.dtype() {
2247 DType::F16 | DType::F32 if use_qmmq => {
2251 x_exp.reshape((nrows, k))?.contiguous()?
2252 }
2253 _ if use_qmmq => x_exp
2254 .reshape((nrows, k))?
2255 .to_dtype(DType::F16)?
2256 .contiguous()?,
2257 DType::BF16 | DType::F16 => x_exp.reshape((nrows, k))?.contiguous()?,
2258 DType::F32 if qt.dp4a_active() => x_exp.reshape((nrows, k))?.contiguous()?,
2261 _ => x_exp
2262 .reshape((nrows, k))?
2263 .to_dtype(DType::F16)?
2264 .contiguous()?,
2265 };
2266 let out_dtype = x.dtype();
2267 let ids_u32 = ids
2272 .reshape((nrows,))?
2273 .to_dtype(crate::DType::U32)?
2274 .contiguous()?;
2275 let (xstore, _) = x_flat.storage_and_layout();
2276 let xr = match &*xstore {
2277 crate::Storage::Rocm(r) => r,
2278 _ => crate::bail!("rocm MoE: x not on rocm after contiguous()"),
2279 };
2280 let (idstore, _) = ids_u32.storage_and_layout();
2281 let idr = match &*idstore {
2282 crate::Storage::Rocm(r) => r,
2283 _ => crate::bail!("rocm MoE: ids not on rocm"),
2284 };
2285 let y = if use_qmmq {
2286 wbank
2287 .device
2288 .moe_qmmq_quant(qt, wbank, xr, idr, nrows, n, k)?
2289 } else {
2290 wbank
2291 .device
2292 .moe_matvec_quant(qt, wbank, xr, idr, nrows, n, k)?
2293 };
2294 let out = crate::tensor::from_storage(
2295 crate::Storage::Rocm(y),
2296 (nrows, n),
2297 crate::op::BackpropOp::none(),
2298 false,
2299 );
2300 out.reshape((t, topk, n))?.to_dtype(out_dtype)
2301 }
2302 #[cfg(feature = "rocm")]
2304 Self::RocmQuant { qtensor, .. } => qtensor.indexed_moe_forward(x, ids),
2305 #[cfg(feature = "vulkan")]
2306 Self::VulkanQuant { qtensor, .. } => qtensor.indexed_moe_forward(x, ids),
2307 #[cfg(feature = "wgpu")]
2308 Self::WgpuQuant { qtensor, .. } => qtensor.indexed_moe_forward(x, ids),
2309 _ => {
2310 panic!("Not implemented!")
2311 }
2312 }
2313 }
2314}
2315
2316impl crate::CustomOp1 for QTensor {
2317 fn name(&self) -> &'static str {
2318 "qmatmul"
2319 }
2320
2321 fn cpu_fwd(
2322 &self,
2323 storage: &crate::CpuStorage,
2324 layout: &crate::Layout,
2325 ) -> Result<(crate::CpuStorage, Shape)> {
2326 if !layout.is_contiguous() {
2327 crate::bail!("input tensor is not contiguous {layout:?}")
2328 }
2329 let src_shape = layout.shape();
2330 let (n, k) = self.shape.dims2()?;
2332 if src_shape.rank() < 2 {
2333 crate::bail!("input tensor has only one dimension {layout:?}")
2334 }
2335 let mut dst_shape = src_shape.dims().to_vec();
2336 let last_k = dst_shape.pop().unwrap();
2337 if last_k != k {
2338 crate::bail!("input tensor {layout:?} incompatible with {:?}", self.shape)
2339 }
2340 dst_shape.push(n);
2341 let dst_shape = Shape::from(dst_shape);
2342 #[allow(clippy::infallible_destructuring_match)]
2343 let self_storage = match &self.storage {
2344 QStorage::Cpu(storage) => storage,
2345 #[cfg(feature = "rocm")]
2346 QStorage::Rocm(..) => crate::bail!("Invalid storage"),
2347 #[cfg(feature = "vulkan")]
2348 QStorage::Vulkan(..) => crate::bail!("Invalid storage"),
2349 #[cfg(feature = "wgpu")]
2350 QStorage::Wgpu(..) => crate::bail!("Invalid storage"),
2351 QStorage::Metal(_) | QStorage::Cuda(_) | QStorage::Stream(_) => {
2352 crate::bail!("Invalid storage")
2353 }
2354 };
2355 match storage.dtype() {
2356 DType::F32 => {
2357 let slice = storage.as_slice::<f32>()?;
2358 let slice =
2359 &slice[layout.start_offset()..layout.start_offset() + src_shape.elem_count()];
2360 let mut dst_storage = vec![0f32; dst_shape.elem_count()];
2361 self_storage.matmul_t(
2362 (dst_shape.elem_count() / n, k, n),
2363 slice,
2364 &mut dst_storage,
2365 )?;
2366 Ok((crate::CpuStorage::F32(dst_storage), dst_shape))
2367 }
2368 DType::F16 => {
2369 let slice = storage.as_slice::<f16>()?;
2370 let slice =
2371 &slice[layout.start_offset()..layout.start_offset() + src_shape.elem_count()];
2372 let mut dst_storage = vec![f16::ZERO; dst_shape.elem_count()];
2373 self_storage.matmul_t_f16(
2374 (dst_shape.elem_count() / n, k, n),
2375 slice,
2376 &mut dst_storage,
2377 )?;
2378 Ok((crate::CpuStorage::F16(dst_storage), dst_shape))
2379 }
2380 _ => crate::bail!("Expected f32/f16"),
2381 }
2382 }
2383
2384 fn metal_fwd(
2385 &self,
2386 storage: &crate::MetalStorage,
2387 layout: &crate::Layout,
2388 ) -> Result<(crate::MetalStorage, Shape)> {
2389 let self_storage = match &self.storage {
2390 QStorage::Metal(metal) => metal,
2391 _ => unreachable!("Cannot call metal matmul on non metal QTensor"),
2392 };
2393 self_storage.fwd(&self.shape, storage, layout)
2394 }
2395
2396 fn cuda_fwd(
2397 &self,
2398 storage: &crate::CudaStorage,
2399 layout: &crate::Layout,
2400 ) -> Result<(crate::CudaStorage, Shape)> {
2401 let self_storage = match &self.storage {
2402 QStorage::Cuda(cuda) => cuda,
2403 _ => unreachable!("Cannot call cuda matmul on non cuda QTensor"),
2404 };
2405 self_storage.fwd(&self.shape, storage, layout)
2406 }
2407}
2408
2409fn dense_matmul(xs: &Tensor, w: &Tensor) -> Result<Tensor> {
2419 let k = *w.dims().last().unwrap();
2420 let rows = xs.elem_count() / k;
2421 if rows == 1 && xs.device().is_rocm() {
2422 let n = w.dim(0)?;
2423 #[cfg(feature = "rocm")]
2424 {
2425 let d = match xs.device() {
2429 Device::Rocm(d) => d.clone(),
2430 _ => unreachable!(),
2431 };
2432 let xs1 = xs.reshape((k,))?.to_dtype(w.dtype())?.contiguous()?;
2433 let w = w.contiguous()?;
2434 let (wstore, _) = w.storage_and_layout();
2435 let wr = match &*wstore {
2436 crate::Storage::Rocm(r) => r,
2437 _ => crate::bail!("dense_matmul: weight not on rocm"),
2438 };
2439 let (xstore, _) = xs1.storage_and_layout();
2440 let xr = match &*xstore {
2441 crate::Storage::Rocm(r) => r,
2442 _ => crate::bail!("dense_matmul: x not on rocm"),
2443 };
2444 let y = d.dense_gemv(wr, xr, n, k)?;
2445 let mut dims = xs.dims().to_vec();
2446 *dims.last_mut().unwrap() = n;
2447 return crate::tensor::from_storage(
2448 crate::Storage::Rocm(y),
2449 dims,
2450 crate::op::BackpropOp::none(),
2451 false,
2452 )
2453 .to_dtype(xs.dtype());
2454 }
2455 #[cfg(not(feature = "rocm"))]
2456 {
2457 let out = xs.reshape((1, k))?.broadcast_mul(w)?.sum(D::Minus1)?;
2458 let mut dims = xs.dims().to_vec();
2459 *dims.last_mut().unwrap() = n;
2460 return out.reshape(dims);
2461 }
2462 }
2463 let w = match *xs.dims() {
2464 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2465 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2466 _ => w.t()?,
2467 };
2468 xs.matmul(&w)
2469}
2470
2471#[cfg(feature = "vulkan")]
2486fn vulkan_prefill_gemm_max_rows(dtype: GgmlDType) -> usize {
2487 match dtype {
2488 GgmlDType::Q4_0 | GgmlDType::Q8_0 | GgmlDType::Q4K | GgmlDType::Q5K | GgmlDType::Q6K => {
2489 usize::MAX
2490 }
2491 _ => 0,
2492 }
2493}
2494
2495impl crate::Module for QMatMul {
2496 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
2497 match self {
2498 #[cfg(feature = "rocm")]
2499 Self::RocmQuant {
2500 qtensor,
2501 wq,
2502 dtype,
2503 n,
2504 k,
2505 } => {
2506 let xs_recovered = if xs.device().is_rocm() {
2510 None
2511 } else {
2512 Some(xs.to_device(&qtensor.device())?)
2513 };
2514 let xs = xs_recovered.as_ref().unwrap_or(xs);
2515 let rows: usize = xs.elem_count() / *k;
2516 #[cfg(feature = "rocm")]
2522 let unified_qt = crate::RocmQuantType::from_ggml(*dtype);
2523 #[cfg(not(feature = "rocm"))]
2524 let unified_qt: Option<()> = None;
2525 #[cfg(feature = "rocm")]
2530 let qmmq_ok = unified_qt.map(|qt| qt.qmmq_capable()).unwrap_or(false);
2531 #[cfg(not(feature = "rocm"))]
2532 let qmmq_ok = false;
2533 if rows == 1 && unified_qt.is_some() {
2534 #[cfg(feature = "rocm")]
2545 let keep_f32 = unified_qt.map(|qt| qt.dp4a_active()).unwrap_or(false);
2546 #[cfg(not(feature = "rocm"))]
2547 let keep_f32 = false;
2548 let xs = match xs.dtype() {
2549 DType::BF16 | DType::F16 => xs.contiguous()?,
2550 DType::F32 if keep_f32 => xs.contiguous()?,
2551 _ => xs.to_dtype(DType::F16)?.contiguous()?,
2552 };
2553 let d = match xs.device() {
2554 Device::Rocm(d) => d,
2555 _ => crate::bail!("RocmQuant input not on rocm"),
2556 };
2557 let y = {
2558 let (store, _) = xs.storage_and_layout();
2559 let xr = match &*store {
2560 crate::Storage::Rocm(r) => r,
2561 _ => crate::bail!("RocmQuant expected rocm storage"),
2562 };
2563 #[cfg(feature = "rocm")]
2564 {
2565 d.matvec_quant(unified_qt.unwrap(), wq, xr, *n, *k)?
2566 }
2567 #[cfg(not(feature = "rocm"))]
2568 {
2569 crate::bail!("rocm feature disabled")
2570 }
2571 };
2572 let mut dims = xs.dims().to_vec();
2573 let last = dims.len() - 1;
2574 dims[last] = *n;
2575 Ok(crate::tensor::from_storage(
2576 crate::Storage::Rocm(y),
2577 dims,
2578 crate::op::BackpropOp::none(),
2579 false,
2580 ))
2581 } else if let Some(qt) = unified_qt.filter(|_| qmmq_ok) {
2582 let xs = xs.to_dtype(DType::F16)?.contiguous()?;
2591 let d = match xs.device() {
2592 Device::Rocm(d) => d,
2593 _ => crate::bail!("RocmQuant input not on rocm"),
2594 };
2595 let m = xs.elem_count() / *k;
2596 let y = {
2597 let (store, _) = xs.storage_and_layout();
2598 let xr = match &*store {
2599 crate::Storage::Rocm(r) => r,
2600 _ => crate::bail!("RocmQuant expected rocm storage"),
2601 };
2602 #[cfg(feature = "rocm")]
2603 {
2604 d.qmmq_quant(qt, xr, wq, m, *n, *k)?
2605 }
2606 #[cfg(not(feature = "rocm"))]
2607 {
2608 let _ = qt;
2609 crate::bail!("rocm feature disabled")
2610 }
2611 };
2612 let mut dims = xs.dims().to_vec();
2613 let last = dims.len() - 1;
2614 dims[last] = *n;
2615 Ok(crate::tensor::from_storage(
2616 crate::Storage::Rocm(y),
2617 dims,
2618 crate::op::BackpropOp::none(),
2619 false,
2620 ))
2621 } else {
2622 let w = qtensor.dequantize_f16(&xs.device())?;
2626 let w = match *xs.dims() {
2627 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2628 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2629 _ => w.t()?,
2630 };
2631 xs.to_dtype(DType::F16)?.matmul(&w)
2632 }
2633 }
2634 #[cfg(feature = "vulkan")]
2635 Self::VulkanQuant {
2636 qtensor,
2637 wq,
2638 dtype,
2639 n,
2640 k,
2641 } => {
2642 let rows: usize = xs.elem_count() / *k;
2643 if rows == 1 {
2644 let xs = xs.contiguous()?;
2647 let d = match xs.device() {
2648 Device::Vulkan(d) => d,
2649 _ => crate::bail!("VulkanQuant input not on vulkan"),
2650 };
2651 let y = {
2652 let (store, _) = xs.storage_and_layout();
2653 let xv = match &*store {
2654 crate::Storage::Vulkan(v) => v,
2655 _ => crate::bail!("VulkanQuant expected vulkan storage"),
2656 };
2657 match dtype {
2658 GgmlDType::Q4_0 => d.matvec_q4_0_gpu(wq, xv, *n, *k)?,
2659 GgmlDType::Q8_0 => d.matvec_q8_gpu(wq, xv, *n, *k)?,
2662 GgmlDType::Q4K => d.matvec_q4k_gpu(wq, xv, *n, *k)?,
2663 GgmlDType::Q5K => d.matvec_q5k_gpu(wq, xv, *n, *k)?,
2664 GgmlDType::Q6K => d.matvec_q6k_gpu(wq, xv, *n, *k)?,
2665 GgmlDType::Q2K => d.matvec_q2k_gpu(wq, xv, *n, *k)?,
2666 GgmlDType::Q3K => d.matvec_q3k_gpu(wq, xv, *n, *k)?,
2667 GgmlDType::IQ4_XS => d.matvec_iq4xs_gpu(wq, xv, *n, *k)?,
2668 GgmlDType::IQ4_NL => d.matvec_iq4nl_gpu(wq, xv, *n, *k)?,
2669 GgmlDType::IQ2_XXS => d.matvec_iq2xxs_gpu(wq, xv, *n, *k)?,
2670 GgmlDType::IQ2_XS => d.matvec_iq2xs_gpu(wq, xv, *n, *k)?,
2671 GgmlDType::IQ1_M => d.matvec_iq1m_gpu(wq, xv, *n, *k)?,
2672 GgmlDType::IQ1_S => d.matvec_iq1s_gpu(wq, xv, *n, *k)?,
2673 GgmlDType::IQ3_S => d.matvec_iq3s_gpu(wq, xv, *n, *k)?,
2674 GgmlDType::IQ3_XXS => d.matvec_iq3xxs_gpu(wq, xv, *n, *k)?,
2675 GgmlDType::IQ2_S => d.matvec_iq2s_gpu(wq, xv, *n, *k)?,
2676 GgmlDType::TQ2_0 => d.matvec_tq2_0_gpu(wq, xv, *n, *k)?,
2677 other => crate::bail!("VulkanQuant: no native matvec for {other:?}"),
2678 }
2679 };
2680 let mut dims = xs.dims().to_vec();
2681 let last = dims.len() - 1;
2682 dims[last] = *n;
2683 Ok(crate::tensor::from_storage(
2684 crate::Storage::Vulkan(y),
2685 dims,
2686 crate::op::BackpropOp::none(),
2687 false,
2688 ))
2689 } else if rows <= vulkan_prefill_gemm_max_rows(*dtype) {
2690 let m = rows;
2699 let xs = xs.contiguous()?;
2700 let d = match xs.device() {
2701 Device::Vulkan(d) => d,
2702 _ => crate::bail!("VulkanQuant input not on vulkan"),
2703 };
2704 let y = {
2705 let (store, _) = xs.storage_and_layout();
2706 let xv = match &*store {
2707 crate::Storage::Vulkan(v) => v,
2708 _ => crate::bail!("VulkanQuant expected vulkan storage"),
2709 };
2710 match dtype {
2711 GgmlDType::Q4_0 => d.matmul_q4_0_gpu(wq, xv, m, *n, *k)?,
2712 GgmlDType::Q8_0 => d.matmul_q8_gpu(wq, xv, m, *n, *k)?,
2713 GgmlDType::Q4K
2718 if *n == 2048
2719 && *k == 2048
2720 && std::env::var_os("VK_MMQ_Q4K").is_some() =>
2721 {
2722 let bank = qtensor.vulkan_moe_bank_split(d, 1, *n, *k)?;
2723 let (xq, xsq, xsum) = d.quantize_act_q8(xv, m, *k)?;
2724 d.mmq_q4k_gpu(&xq, &xsq, &xsum, bank.as_ref(), m, *n)?
2725 }
2726 GgmlDType::Q4K => d.matmul_q4k_gpu(wq, xv, m, *n, *k)?,
2727 GgmlDType::Q5K => d.matmul_q5k_gpu(wq, xv, m, *n, *k)?,
2728 GgmlDType::Q6K => d.matmul_q6k_gpu(wq, xv, m, *n, *k)?,
2729 other => crate::bail!("VulkanQuant: no native matmul for {other:?}"),
2730 }
2731 };
2732 let mut dims = xs.dims().to_vec();
2733 let last = dims.len() - 1;
2734 dims[last] = *n;
2735 Ok(crate::tensor::from_storage(
2736 crate::Storage::Vulkan(y),
2737 dims,
2738 crate::op::BackpropOp::none(),
2739 false,
2740 ))
2741 } else {
2742 let w = qtensor.dequantize(&xs.device())?;
2747 let w = match *xs.dims() {
2748 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2749 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2750 _ => w.t()?,
2751 };
2752 xs.matmul(&w)
2753 }
2754 }
2755 #[cfg(feature = "wgpu")]
2756 Self::WgpuQuant {
2757 qtensor,
2758 wq,
2759 dtype,
2760 n,
2761 k,
2762 } => {
2763 let rows: usize = xs.elem_count() / *k;
2764 if rows == 1 {
2765 let xs = xs.contiguous()?;
2768 let d = match xs.device() {
2769 Device::Wgpu(d) => d,
2770 _ => crate::bail!("WgpuQuant input not on wgpu"),
2771 };
2772 let y = {
2773 let (store, _) = xs.storage_and_layout();
2774 let xv = match &*store {
2775 crate::Storage::Wgpu(v) => v,
2776 _ => crate::bail!("WgpuQuant expected wgpu storage"),
2777 };
2778 match dtype {
2779 GgmlDType::Q4_0 => d.matvec_q4_0_gpu(wq, xv, *n, *k)?,
2780 GgmlDType::Q8_0 => d.matvec_q8_0_gpu(wq, xv, *n, *k)?,
2781 GgmlDType::Q4K => d.matvec_q4k_gpu(wq, xv, *n, *k)?,
2782 other => crate::bail!("WgpuQuant: no native matvec for {other:?}"),
2783 }
2784 };
2785 let mut dims = xs.dims().to_vec();
2786 let last = dims.len() - 1;
2787 dims[last] = *n;
2788 Ok(crate::tensor::from_storage(
2789 crate::Storage::Wgpu(y),
2790 dims,
2791 crate::op::BackpropOp::none(),
2792 false,
2793 ))
2794 } else {
2795 let w = qtensor.dequantize(&xs.device())?;
2797 let w = match *xs.dims() {
2798 [b1, b2, _, _] => w.broadcast_left((b1, b2))?.t()?,
2799 [bsize, _, _] => w.broadcast_left(bsize)?.t()?,
2800 _ => w.t()?,
2801 };
2802 xs.matmul(&w)
2803 }
2804 }
2805 Self::QTensor(t) => xs.apply_op1_no_bwd(t.as_ref()),
2806 Self::Tensor(w) => dense_matmul(xs, w),
2807 Self::TensorF16(w) => {
2808 let in_dtype = xs.dtype();
2809 dense_matmul(&xs.to_dtype(DType::F16)?, w)?.to_dtype(in_dtype)
2810 }
2811 }
2812 }
2813}