1use std::sync::Arc;
9use anyhow::Result;
10use candle_core::{DType, Tensor as CandleTensor, Device as CandleDevice};
11use candle_nn::ops as candle_ops;
12
13#[derive(Debug, Clone)]
15pub struct Tensor {
16 data: Arc<dyn TensorStorage>,
18 shape: Vec<usize>,
20 dtype: DataType,
22 device: Device,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum DataType {
29 Float32,
30 Float16,
31 BFloat16,
32 Int32,
33 Int64,
34 Int8,
35 Bool,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Hash)]
40pub enum Device {
41 CPU,
42 CUDA(usize), Metal(usize),
44}
45
46pub trait TensorStorage: Send + Sync + std::fmt::Debug {
48 fn data_ptr(&self) -> *const u8;
50
51 fn data_ptr_mut(&mut self) -> *mut u8;
53
54 fn size_bytes(&self) -> usize;
56
57 fn device(&self) -> &Device;
59
60 fn to_device(&self, device: &Device) -> Result<Arc<dyn TensorStorage>>;
62
63 fn clone_storage(&self) -> Arc<dyn TensorStorage>;
65
66 fn as_candle_tensor(&self) -> Option<&CandleTensor> {
68 None
69 }
70}
71
72#[derive(Debug)]
74pub struct CpuStorage {
75 data: Vec<u8>,
76 device: Device,
77}
78
79#[derive(Debug)]
81pub struct GpuStorage {
82 #[allow(dead_code)]
83 ptr: *mut u8,
84 size: usize,
85 device: Device,
86}
87
88#[derive(Debug, Clone)]
90pub struct CandleStorage {
91 tensor: CandleTensor,
92}
93
94impl CandleStorage {
95 pub fn new(tensor: CandleTensor) -> Self {
97 Self { tensor }
98 }
99
100 pub fn tensor(&self) -> &CandleTensor {
102 &self.tensor
103 }
104
105 pub fn tensor_mut(&mut self) -> &mut CandleTensor {
107 &mut self.tensor
108 }
109
110 pub fn zeros(shape: &[usize], dtype: DataType, device: &Device) -> Result<Self> {
112 let candle_device = device.to_candle();
113 let candle_dtype = dtype.to_candle();
114 let tensor = CandleTensor::zeros(shape, candle_dtype, &candle_device)?;
115 Ok(Self { tensor })
116 }
117
118 pub fn ones(shape: &[usize], dtype: DataType, device: &Device) -> Result<Self> {
120 let candle_device = device.to_candle();
121 let candle_dtype = dtype.to_candle();
122 let tensor = CandleTensor::ones(shape, candle_dtype, &candle_device)?;
123 Ok(Self { tensor })
124 }
125
126 pub fn from_f32_slice(data: &[f32], shape: &[usize], device: &Device) -> Result<Self> {
128 let candle_device = device.to_candle();
129 let tensor = CandleTensor::from_slice(data, shape, &candle_device)?;
130 Ok(Self { tensor })
131 }
132
133 pub fn from_i64_slice(data: &[i64], shape: &[usize], device: &Device) -> Result<Self> {
135 let candle_device = device.to_candle();
136 let tensor = CandleTensor::from_slice(data, shape, &candle_device)?;
137 Ok(Self { tensor })
138 }
139}
140
141impl TensorStorage for CandleStorage {
142 fn data_ptr(&self) -> *const u8 {
143 std::ptr::null() }
147
148 fn data_ptr_mut(&mut self) -> *mut u8 {
149 std::ptr::null_mut() }
151
152 fn size_bytes(&self) -> usize {
153 self.tensor.elem_count() * self.tensor.dtype().size_in_bytes()
154 }
155
156 fn device(&self) -> &Device {
157 static CPU_DEVICE: Device = Device::CPU;
160 &CPU_DEVICE
161 }
162
163 fn to_device(&self, device: &Device) -> Result<Arc<dyn TensorStorage>> {
164 let candle_device = device.to_candle();
165 let new_tensor = self.tensor.to_device(&candle_device)?;
166 Ok(Arc::new(CandleStorage::new(new_tensor)))
167 }
168
169 fn clone_storage(&self) -> Arc<dyn TensorStorage> {
170 Arc::new(self.clone())
171 }
172
173 fn as_candle_tensor(&self) -> Option<&CandleTensor> {
174 Some(&self.tensor)
175 }
176}
177
178pub trait TensorOps: Send + Sync {
180 fn matmul(&self, a: &Tensor, b: &Tensor) -> Result<Tensor>;
182
183 fn add(&self, a: &Tensor, b: &Tensor) -> Result<Tensor>;
185
186 fn mul(&self, a: &Tensor, b: &Tensor) -> Result<Tensor>;
188
189 fn attention(
191 &self,
192 query: &Tensor,
193 key: &Tensor,
194 value: &Tensor,
195 mask: Option<&Tensor>,
196 scale: Option<f32>,
197 ) -> Result<Tensor>;
198
199 fn layer_norm(
201 &self,
202 input: &Tensor,
203 weight: &Tensor,
204 bias: Option<&Tensor>,
205 eps: f32,
206 ) -> Result<Tensor>;
207
208 fn gelu(&self, input: &Tensor) -> Result<Tensor>;
210
211 fn silu(&self, input: &Tensor) -> Result<Tensor>;
213
214 fn softmax(&self, input: &Tensor, dim: isize) -> Result<Tensor>;
216
217 fn embedding(&self, indices: &Tensor, weight: &Tensor) -> Result<Tensor>;
219
220 fn zeros(&self, shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor>;
222
223 fn randn(&self, shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor>;
225
226 fn exp(&self, input: &Tensor) -> Result<Tensor>;
228
229 fn normalize(&self, input: &Tensor, p: i32, dim: i32) -> Result<Tensor>;
231
232 fn concat(&self, tensors: &[&Tensor], dim: usize) -> Result<Tensor>;
234
235 fn rms_norm(&self, input: &Tensor, weight: &Tensor, eps: f32) -> Result<Tensor>;
237
238 fn scale(&self, input: &Tensor, factor: f32) -> Result<Tensor>;
240
241 fn sigmoid(&self, input: &Tensor) -> Result<Tensor>;
243
244 fn topk(&self, input: &Tensor, k: usize, dim: i64) -> Result<(Tensor, Tensor)>;
246
247 fn conv1d(&self, input: &Tensor, weight: &Tensor, bias: Option<&Tensor>, stride: usize, padding: usize) -> Result<Tensor>;
249
250 fn tanh(&self, input: &Tensor) -> Result<Tensor>;
252
253 fn sub(&self, a: &Tensor, b: &Tensor) -> Result<Tensor>;
255
256 fn clamp(&self, input: &Tensor, min: f32, max: f32) -> Result<Tensor>;
258
259 fn gather(&self, input: &Tensor, dim: usize, indices: &Tensor) -> Result<Tensor>;
261
262 fn scatter(&self, input: &Tensor, dim: usize, indices: &Tensor, src: &Tensor) -> Result<Tensor>;
264
265 fn flash_attention(
271 &self,
272 query: &Tensor, key: &Tensor, value: &Tensor, scale: f32,
276 causal: bool,
277 ) -> Result<Tensor>;
278
279 fn fused_swiglu(
282 &self,
283 gate: &Tensor,
284 up: &Tensor,
285 ) -> Result<Tensor>;
286
287 fn fused_residual_rms_norm(
290 &self,
291 residual: &Tensor,
292 hidden: &Tensor,
293 weight: &Tensor,
294 eps: f32,
295 ) -> Result<Tensor>;
296}
297
298pub struct TensorDispatcher {
300 cpu_ops: Box<dyn TensorOps>,
301 #[allow(dead_code)]
302 gpu_ops: Option<Box<dyn TensorOps>>,
303}
304
305impl Tensor {
306 pub fn new(
308 shape: Vec<usize>,
309 dtype: DataType,
310 device: Device,
311 data: Arc<dyn TensorStorage>
312 ) -> Self {
313 Self {
314 data,
315 shape,
316 dtype,
317 device,
318 }
319 }
320
321 pub fn shape(&self) -> &[usize] {
323 &self.shape
324 }
325
326 pub fn dtype(&self) -> DataType {
328 self.dtype
329 }
330
331 pub fn device(&self) -> &Device {
333 &self.device
334 }
335
336 pub fn numel(&self) -> usize {
338 self.shape.iter().product()
339 }
340
341 pub fn to_device(&self, device: &Device) -> Result<Self> {
343 if &self.device == device {
344 return Ok(self.clone());
345 }
346
347 let new_storage = self.data.to_device(device)?;
348 Ok(Self {
349 data: new_storage,
350 shape: self.shape.clone(),
351 dtype: self.dtype,
352 device: device.clone(),
353 })
354 }
355
356 pub fn reshape(&self, new_shape: &[usize]) -> Result<Self> {
358 let old_numel = self.numel();
359 let new_numel: usize = new_shape.iter().product();
360
361 if old_numel != new_numel {
362 return Err(anyhow::anyhow!(
363 "Cannot reshape tensor: {} elements to {} elements",
364 old_numel, new_numel
365 ));
366 }
367
368 Ok(Self {
369 data: self.data.clone(),
370 shape: new_shape.to_vec(),
371 dtype: self.dtype,
372 device: self.device.clone(),
373 })
374 }
375
376 pub fn narrow(&self, dim: usize, start: usize, length: usize) -> Result<Self> {
381 let candle_tensor = self.to_candle()?;
382 let narrowed = candle_tensor.narrow(dim, start, length)?;
383 Ok(Self::from_candle(narrowed))
384 }
385
386 pub fn from_candle(candle_tensor: CandleTensor) -> Self {
388 let shape = candle_tensor.dims().to_vec();
389 let dtype = DataType::from_candle(candle_tensor.dtype());
390 let device = Device::from_candle(&candle_tensor.device());
391 let storage = Arc::new(CandleStorage::new(candle_tensor));
392 Self {
393 data: storage,
394 shape,
395 dtype,
396 device,
397 }
398 }
399
400 pub fn as_candle(&self) -> Option<&CandleTensor> {
402 self.data.as_candle_tensor()
404 }
405
406 pub fn to_candle(&self) -> Result<CandleTensor> {
408 if let Some(candle_tensor) = self.data.as_candle_tensor() {
410 let candle_shape = candle_tensor.dims();
412 if candle_shape != &self.shape[..] {
413 let shape_slice: &[usize] = &self.shape;
415 return Ok(candle_tensor.reshape(shape_slice)?);
416 }
417 return Ok(candle_tensor.clone());
418 }
419
420 let candle_device = self.device.to_candle();
422 let candle_dtype = self.dtype.to_candle();
423 let shape: &[usize] = &self.shape;
424
425 let ptr = self.data.data_ptr();
427 if ptr.is_null() {
428 return Ok(CandleTensor::zeros(shape, candle_dtype, &candle_device)?);
430 }
431
432 let size_bytes = self.data.size_bytes();
433 let data_slice = unsafe { std::slice::from_raw_parts(ptr, size_bytes) };
434
435 match self.dtype {
437 DataType::Float32 => {
438 let float_data: &[f32] = bytemuck::cast_slice(data_slice);
439 Ok(CandleTensor::from_slice(float_data, shape, &candle_device)?)
440 }
441 DataType::Int64 => {
442 let int_data: &[i64] = bytemuck::cast_slice(data_slice);
443 Ok(CandleTensor::from_slice(int_data, shape, &candle_device)?)
444 }
445 DataType::Int32 => {
446 let int_data: &[i32] = bytemuck::cast_slice(data_slice);
448 let int64_data: Vec<i64> = int_data.iter().map(|&x| x as i64).collect();
449 Ok(CandleTensor::from_slice(&int64_data, shape, &candle_device)?)
450 }
451 _ => {
452 Ok(CandleTensor::zeros(shape, candle_dtype, &candle_device)?)
454 }
455 }
456 }
457
458 pub fn zeros_candle(shape: &[usize], dtype: DataType, device: &Device) -> Result<Self> {
460 let storage = CandleStorage::zeros(shape, dtype, device)?;
461 Ok(Self {
462 data: Arc::new(storage.clone()),
463 shape: shape.to_vec(),
464 dtype,
465 device: device.clone(),
466 })
467 }
468
469 pub fn from_f32_slice(data: &[f32], shape: &[usize], device: &Device) -> Result<Self> {
471 let storage = CandleStorage::from_f32_slice(data, shape, device)?;
472 Ok(Self {
473 data: Arc::new(storage),
474 shape: shape.to_vec(),
475 dtype: DataType::Float32,
476 device: device.clone(),
477 })
478 }
479
480 pub fn from_i64_slice(data: &[i64], shape: &[usize], device: &Device) -> Result<Self> {
482 let storage = CandleStorage::from_i64_slice(data, shape, device)?;
483 Ok(Self {
484 data: Arc::new(storage),
485 shape: shape.to_vec(),
486 dtype: DataType::Int64,
487 device: device.clone(),
488 })
489 }
490
491 pub fn to_vec_f32(&self) -> Result<Vec<f32>> {
493 let candle_tensor = self.to_candle()?;
494 let data = candle_tensor.to_vec1::<f32>()?;
495 Ok(data)
496 }
497
498 pub fn transpose(&self) -> Result<Self> {
502 let candle_tensor = self.to_candle()?;
503 let dims = candle_tensor.dims();
504 if dims.len() < 2 {
505 return Err(anyhow::anyhow!("Cannot transpose tensor with less than 2 dimensions"));
506 }
507 let transposed = candle_tensor.t()?;
508 Ok(Self::from_candle(transposed))
509 }
510
511 pub fn transpose_dims(&self, dim0: usize, dim1: usize) -> Result<Self> {
513 let candle_tensor = self.to_candle()?;
514 let transposed = candle_tensor.transpose(dim0, dim1)?;
515 Ok(Self::from_candle(transposed))
516 }
517}
518
519impl CpuStorage {
520 pub fn new(data: Vec<u8>, device: Device) -> Self {
521 Self { data, device }
522 }
523
524 pub fn zeros(size_bytes: usize) -> Self {
525 Self {
526 data: vec![0u8; size_bytes],
527 device: Device::CPU,
528 }
529 }
530}
531
532impl TensorStorage for CpuStorage {
533 fn data_ptr(&self) -> *const u8 {
534 self.data.as_ptr()
535 }
536
537 fn data_ptr_mut(&mut self) -> *mut u8 {
538 self.data.as_mut_ptr()
539 }
540
541 fn size_bytes(&self) -> usize {
542 self.data.len()
543 }
544
545 fn device(&self) -> &Device {
546 &self.device
547 }
548
549 fn to_device(&self, device: &Device) -> Result<Arc<dyn TensorStorage>> {
550 match device {
551 Device::CPU => Ok(Arc::new(self.clone())),
552 Device::CUDA(_) | Device::Metal(_) => {
553 Err(anyhow::anyhow!("GPU transfer not implemented yet"))
555 }
556 }
557 }
558
559 fn clone_storage(&self) -> Arc<dyn TensorStorage> {
560 Arc::new(self.clone())
561 }
562}
563
564impl Clone for CpuStorage {
565 fn clone(&self) -> Self {
566 Self {
567 data: self.data.clone(),
568 device: self.device.clone(),
569 }
570 }
571}
572
573unsafe impl Send for GpuStorage {}
574unsafe impl Sync for GpuStorage {}
575
576impl TensorStorage for GpuStorage {
577 fn data_ptr(&self) -> *const u8 {
578 self.ptr
579 }
580
581 fn data_ptr_mut(&mut self) -> *mut u8 {
582 self.ptr
583 }
584
585 fn size_bytes(&self) -> usize {
586 self.size
587 }
588
589 fn device(&self) -> &Device {
590 &self.device
591 }
592
593 fn to_device(&self, device: &Device) -> Result<Arc<dyn TensorStorage>> {
594 match device {
595 Device::CPU => {
596 let cpu_storage = Arc::new(CpuStorage::zeros(self.size));
598 Ok(cpu_storage)
599 }
600 Device::CUDA(_) | Device::Metal(_) => {
601 let gpu_storage = Arc::new(GpuStorage {
603 ptr: std::ptr::null_mut(),
604 size: self.size,
605 device: device.clone(),
606 });
607 Ok(gpu_storage)
608 }
609 }
610 }
611
612 fn clone_storage(&self) -> Arc<dyn TensorStorage> {
613 Arc::new(GpuStorage {
615 ptr: self.ptr,
616 size: self.size,
617 device: self.device.clone(),
618 })
619 }
620}
621
622impl TensorDispatcher {
623 pub fn new() -> Self {
624 Self {
625 cpu_ops: Box::new(CpuTensorOpsImpl::new()),
626 gpu_ops: Some(Box::new(GpuTensorOpsImpl::new())), }
628 }
629
630 fn get_ops(&self, tensors: &[&Tensor]) -> &dyn TensorOps {
632 for tensor in tensors {
634 match tensor.device() {
635 Device::CPU => continue,
636 Device::CUDA(_) | Device::Metal(_) => {
637 if let Some(gpu_ops) = &self.gpu_ops {
639 return gpu_ops.as_ref();
640 }
641 }
642 }
643 }
644 self.cpu_ops.as_ref()
645 }
646}
647
648impl TensorOps for TensorDispatcher {
649 fn matmul(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
650 let ops = self.get_ops(&[a, b]);
651 ops.matmul(a, b)
652 }
653
654 fn add(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
655 let ops = self.get_ops(&[a, b]);
656 ops.add(a, b)
657 }
658
659 fn mul(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
660 let ops = self.get_ops(&[a, b]);
661 ops.mul(a, b)
662 }
663
664 fn attention(
665 &self,
666 query: &Tensor,
667 key: &Tensor,
668 value: &Tensor,
669 mask: Option<&Tensor>,
670 scale: Option<f32>,
671 ) -> Result<Tensor> {
672 let tensors = if let Some(m) = mask {
673 vec![query, key, value, m]
674 } else {
675 vec![query, key, value]
676 };
677 let ops = self.get_ops(&tensors);
678 ops.attention(query, key, value, mask, scale)
679 }
680
681 fn layer_norm(
682 &self,
683 input: &Tensor,
684 weight: &Tensor,
685 bias: Option<&Tensor>,
686 eps: f32,
687 ) -> Result<Tensor> {
688 let tensors = if let Some(b) = bias {
689 vec![input, weight, b]
690 } else {
691 vec![input, weight]
692 };
693 let ops = self.get_ops(&tensors);
694 ops.layer_norm(input, weight, bias, eps)
695 }
696
697 fn gelu(&self, input: &Tensor) -> Result<Tensor> {
698 let ops = self.get_ops(&[input]);
699 ops.gelu(input)
700 }
701
702 fn silu(&self, input: &Tensor) -> Result<Tensor> {
703 let ops = self.get_ops(&[input]);
704 ops.silu(input)
705 }
706
707 fn softmax(&self, input: &Tensor, dim: isize) -> Result<Tensor> {
708 let ops = self.get_ops(&[input]);
709 ops.softmax(input, dim)
710 }
711
712 fn embedding(&self, indices: &Tensor, weight: &Tensor) -> Result<Tensor> {
713 let ops = self.get_ops(&[indices, weight]);
714 ops.embedding(indices, weight)
715 }
716
717 fn zeros(&self, shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor> {
718 match device {
719 Device::CPU => self.cpu_ops.zeros(shape, dtype, device),
720 Device::CUDA(_) | Device::Metal(_) => {
721 self.cpu_ops.zeros(shape, dtype, &Device::CPU)
723 }
724 }
725 }
726
727 fn randn(&self, shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor> {
728 match device {
729 Device::CPU => self.cpu_ops.randn(shape, dtype, device),
730 Device::CUDA(_) | Device::Metal(_) => {
731 self.cpu_ops.randn(shape, dtype, &Device::CPU)
733 }
734 }
735 }
736
737 fn exp(&self, input: &Tensor) -> Result<Tensor> {
738 let ops = self.get_ops(&[input]);
739 ops.exp(input)
740 }
741
742 fn normalize(&self, input: &Tensor, p: i32, dim: i32) -> Result<Tensor> {
743 let ops = self.get_ops(&[input]);
744 ops.normalize(input, p, dim)
745 }
746
747 fn concat(&self, tensors: &[&Tensor], dim: usize) -> Result<Tensor> {
748 let ops = self.get_ops(tensors);
749 ops.concat(tensors, dim)
750 }
751
752 fn rms_norm(&self, input: &Tensor, weight: &Tensor, eps: f32) -> Result<Tensor> {
753 let ops = self.get_ops(&[input, weight]);
754 ops.rms_norm(input, weight, eps)
755 }
756
757 fn scale(&self, input: &Tensor, factor: f32) -> Result<Tensor> {
758 let ops = self.get_ops(&[input]);
759 ops.scale(input, factor)
760 }
761
762 fn sigmoid(&self, input: &Tensor) -> Result<Tensor> {
763 let ops = self.get_ops(&[input]);
764 ops.sigmoid(input)
765 }
766
767 fn topk(&self, input: &Tensor, k: usize, dim: i64) -> Result<(Tensor, Tensor)> {
768 let ops = self.get_ops(&[input]);
769 ops.topk(input, k, dim)
770 }
771
772 fn conv1d(&self, input: &Tensor, weight: &Tensor, bias: Option<&Tensor>, stride: usize, padding: usize) -> Result<Tensor> {
773 let tensors = if let Some(b) = bias {
774 vec![input, weight, b]
775 } else {
776 vec![input, weight]
777 };
778 let ops = self.get_ops(&tensors);
779 ops.conv1d(input, weight, bias, stride, padding)
780 }
781
782 fn tanh(&self, input: &Tensor) -> Result<Tensor> {
783 let ops = self.get_ops(&[input]);
784 ops.tanh(input)
785 }
786
787 fn sub(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
788 let ops = self.get_ops(&[a, b]);
789 ops.sub(a, b)
790 }
791
792 fn clamp(&self, input: &Tensor, min: f32, max: f32) -> Result<Tensor> {
793 let ops = self.get_ops(&[input]);
794 ops.clamp(input, min, max)
795 }
796
797 fn gather(&self, input: &Tensor, dim: usize, indices: &Tensor) -> Result<Tensor> {
798 let ops = self.get_ops(&[input, indices]);
799 ops.gather(input, dim, indices)
800 }
801
802 fn scatter(&self, input: &Tensor, dim: usize, indices: &Tensor, src: &Tensor) -> Result<Tensor> {
803 let ops = self.get_ops(&[input, indices, src]);
804 ops.scatter(input, dim, indices, src)
805 }
806
807 fn flash_attention(&self, query: &Tensor, key: &Tensor, value: &Tensor, scale: f32, causal: bool) -> Result<Tensor> {
809 let ops = self.get_ops(&[query, key, value]);
810 ops.flash_attention(query, key, value, scale, causal)
811 }
812
813 fn fused_swiglu(&self, gate: &Tensor, up: &Tensor) -> Result<Tensor> {
814 let ops = self.get_ops(&[gate, up]);
815 ops.fused_swiglu(gate, up)
816 }
817
818 fn fused_residual_rms_norm(&self, residual: &Tensor, hidden: &Tensor, weight: &Tensor, eps: f32) -> Result<Tensor> {
819 let ops = self.get_ops(&[residual, hidden, weight]);
820 ops.fused_residual_rms_norm(residual, hidden, weight, eps)
821 }
822}
823
824pub struct CpuTensorOpsImpl;
826
827pub struct GpuTensorOpsImpl;
829
830impl CpuTensorOpsImpl {
831 pub fn new() -> Self {
832 Self
833 }
834}
835
836impl GpuTensorOpsImpl {
837 pub fn new() -> Self {
838 Self
839 }
840}
841
842impl TensorOps for CpuTensorOpsImpl {
843 fn matmul(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
844 let a_candle = a.to_candle()?;
846 let b_candle = b.to_candle()?;
847
848 let a_dims = a_candle.dims();
849 let b_dims = b_candle.dims();
850
851 let result_candle = match (a_dims.len(), b_dims.len()) {
853 (2, 2) => {
854 a_candle.matmul(&b_candle)?
856 }
857 (3, 2) => {
858 let (batch, m, k) = (a_dims[0], a_dims[1], a_dims[2]);
861 let n = b_dims[1];
862
863 let a_flat = a_candle.reshape(&[batch * m, k])?;
865
866 let result_flat = a_flat.matmul(&b_candle)?;
868
869 result_flat.reshape(&[batch, m, n])?
871 }
872 (3, 3) => {
873 a_candle.matmul(&b_candle)?
875 }
876 (2, 3) => {
877 let (m, k) = (a_dims[0], a_dims[1]);
880 let batch = b_dims[0];
881
882 let a_expanded = a_candle.unsqueeze(0)?.broadcast_as(&[batch, m, k])?;
883 a_expanded.matmul(&b_candle)?
884 }
885 _ => {
886 a_candle.matmul(&b_candle)?
888 }
889 };
890
891 Ok(Tensor::from_candle(result_candle))
892 }
893
894 fn add(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
895 let a_candle = a.to_candle()?;
897 let b_candle = b.to_candle()?;
898
899 let result_candle = if a.shape() == b.shape() {
901 (&a_candle + &b_candle)?
902 } else {
903 a_candle.broadcast_add(&b_candle)?
905 };
906
907 Ok(Tensor::from_candle(result_candle))
908 }
909
910 fn mul(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
911 let a_candle = a.to_candle()?;
913 let b_candle = b.to_candle()?;
914
915 let result_candle = if a.shape() == b.shape() {
917 (&a_candle * &b_candle)?
918 } else {
919 a_candle.broadcast_mul(&b_candle)?
920 };
921
922 Ok(Tensor::from_candle(result_candle))
923 }
924
925 fn attention(
926 &self,
927 query: &Tensor,
928 key: &Tensor,
929 value: &Tensor,
930 mask: Option<&Tensor>,
931 scale: Option<f32>,
932 ) -> Result<Tensor> {
933 let q = query.to_candle()?;
935 let k = key.to_candle()?;
936 let v = value.to_candle()?;
937
938 let d_k = *q.dims().last().unwrap_or(&1);
940 let scale_factor = scale.unwrap_or(1.0 / (d_k as f32).sqrt());
941
942 let k_t = k.transpose(k.dims().len() - 2, k.dims().len() - 1)?;
944 let scores = q.matmul(&k_t)?;
945
946 let scaled_scores = (scores * scale_factor as f64)?;
948
949 let masked_scores = if let Some(m) = mask {
951 let mask_candle = m.to_candle()?;
952 let neg_inf = CandleTensor::new(&[-1e9f32], &CandleDevice::Cpu)?;
954 let neg_inf = neg_inf.broadcast_as(scaled_scores.dims())?;
955 let mask_expanded = mask_candle.broadcast_as(scaled_scores.dims())?;
956 let mask_f32 = mask_expanded.to_dtype(DType::F32)?;
958 let inverted_mask = (1.0 - &mask_f32)?;
959 ((&mask_f32 * &scaled_scores)? + (&inverted_mask * &neg_inf)?)?
960 } else {
961 scaled_scores
962 };
963
964 let attention_weights = candle_ops::softmax_last_dim(&masked_scores)?;
966
967 let result = attention_weights.matmul(&v)?;
969
970 Ok(Tensor::from_candle(result))
971 }
972
973 fn layer_norm(
974 &self,
975 input: &Tensor,
976 weight: &Tensor,
977 bias: Option<&Tensor>,
978 eps: f32,
979 ) -> Result<Tensor> {
980 let x = input.to_candle()?;
981 let w = weight.to_candle()?;
982
983 let last_dim = x.dims().len() - 1;
985
986 let mean = x.mean_keepdim(last_dim)?;
988
989 let x_centered = x.broadcast_sub(&mean)?;
991 let variance = x_centered.sqr()?.mean_keepdim(last_dim)?;
992
993 let std = (variance + eps as f64)?.sqrt()?;
995 let normalized = x_centered.broadcast_div(&std)?;
996
997 let scaled = normalized.broadcast_mul(&w)?;
999
1000 let result = if let Some(b) = bias {
1001 let b_candle = b.to_candle()?;
1002 scaled.broadcast_add(&b_candle)?
1003 } else {
1004 scaled
1005 };
1006
1007 Ok(Tensor::from_candle(result))
1008 }
1009
1010 fn gelu(&self, input: &Tensor) -> Result<Tensor> {
1011 let x = input.to_candle()?;
1012 let result = x.gelu()?;
1013 Ok(Tensor::from_candle(result))
1014 }
1015
1016 fn silu(&self, input: &Tensor) -> Result<Tensor> {
1017 let x = input.to_candle()?;
1018 let result = x.silu()?;
1019 Ok(Tensor::from_candle(result))
1020 }
1021
1022 fn softmax(&self, input: &Tensor, dim: isize) -> Result<Tensor> {
1023 let x = input.to_candle()?;
1024
1025 let ndims = x.dims().len() as isize;
1027 let actual_dim = if dim < 0 { ndims + dim } else { dim } as usize;
1028
1029 let result = if actual_dim == x.dims().len() - 1 {
1030 candle_ops::softmax_last_dim(&x)?
1031 } else {
1032 candle_ops::softmax(&x, actual_dim)?
1033 };
1034
1035 Ok(Tensor::from_candle(result))
1036 }
1037
1038 fn embedding(&self, indices: &Tensor, weight: &Tensor) -> Result<Tensor> {
1039 let idx = indices.to_candle()?;
1040 let w = weight.to_candle()?;
1041
1042 let idx_u32 = idx.to_dtype(DType::U32)?;
1044
1045 let orig_shape = idx_u32.dims().to_vec();
1047 let flat_idx = idx_u32.flatten_all()?;
1048
1049 let embedded = w.embedding(&flat_idx)?;
1051
1052 let embed_dim = w.dims()[1];
1054 let mut result_shape = orig_shape;
1055 result_shape.push(embed_dim);
1056
1057 let result = embedded.reshape(result_shape.as_slice())?;
1058
1059 Ok(Tensor::from_candle(result))
1060 }
1061
1062 fn zeros(&self, shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor> {
1063 let storage = CandleStorage::zeros(shape, dtype, device)?;
1064 Ok(Tensor::new(
1065 shape.to_vec(),
1066 dtype,
1067 device.clone(),
1068 Arc::new(storage),
1069 ))
1070 }
1071
1072 fn randn(&self, shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor> {
1073 let candle_device = device.to_candle();
1074 let candle_dtype = dtype.to_candle();
1075
1076 let result = CandleTensor::randn(0f32, 1f32, shape, &candle_device)?
1078 .to_dtype(candle_dtype)?;
1079
1080 Ok(Tensor::from_candle(result))
1081 }
1082
1083 fn exp(&self, input: &Tensor) -> Result<Tensor> {
1084 let x = input.to_candle()?;
1085 let result = x.exp()?;
1086 Ok(Tensor::from_candle(result))
1087 }
1088
1089 fn normalize(&self, input: &Tensor, p: i32, dim: i32) -> Result<Tensor> {
1090 let x = input.to_candle()?;
1091
1092 let actual_dim = if dim < 0 {
1094 x.dims().len() as i32 + dim
1095 } else {
1096 dim
1097 } as usize;
1098
1099 if p == 2 {
1100 let squared = x.sqr()?;
1102 let sum_squared = squared.sum_keepdim(actual_dim)?;
1103 let norm = sum_squared.sqrt()?;
1104 let normalized = x.broadcast_div(&norm)?;
1105 Ok(Tensor::from_candle(normalized))
1106 } else {
1107 Ok(input.clone())
1109 }
1110 }
1111
1112 fn concat(&self, tensors: &[&Tensor], dim: usize) -> Result<Tensor> {
1113 if tensors.is_empty() {
1114 return Err(anyhow::anyhow!("Cannot concatenate empty tensor list"));
1115 }
1116
1117 let candle_tensors: Result<Vec<_>> = tensors.iter()
1119 .map(|t| t.to_candle())
1120 .collect();
1121 let candle_tensors = candle_tensors?;
1122
1123 let refs: Vec<&CandleTensor> = candle_tensors.iter().collect();
1125
1126 let result = CandleTensor::cat(&refs, dim)?;
1127 Ok(Tensor::from_candle(result))
1128 }
1129
1130 fn rms_norm(&self, input: &Tensor, weight: &Tensor, eps: f32) -> Result<Tensor> {
1131 let x = input.to_candle()?;
1132 let w = weight.to_candle()?;
1133
1134 let last_dim = x.dims().len() - 1;
1136 let x_squared = x.sqr()?;
1137 let mean_squared = x_squared.mean_keepdim(last_dim)?;
1138 let rms = (mean_squared + eps as f64)?.sqrt()?;
1139 let normalized = x.broadcast_div(&rms)?;
1140 let result = normalized.broadcast_mul(&w)?;
1141
1142 Ok(Tensor::from_candle(result))
1143 }
1144
1145 fn scale(&self, input: &Tensor, factor: f32) -> Result<Tensor> {
1146 let x = input.to_candle()?;
1147 let result = (x * factor as f64)?;
1148 Ok(Tensor::from_candle(result))
1149 }
1150
1151 fn sigmoid(&self, input: &Tensor) -> Result<Tensor> {
1152 let x = input.to_candle()?;
1153 let neg_x = x.neg()?;
1155 let exp_neg_x = neg_x.exp()?;
1156 let one_plus_exp = (exp_neg_x + 1.0)?;
1157 let result = one_plus_exp.recip()?;
1158 Ok(Tensor::from_candle(result))
1159 }
1160
1161 fn topk(&self, input: &Tensor, k: usize, dim: i64) -> Result<(Tensor, Tensor)> {
1162 let x = input.to_candle()?;
1163 let ndims = x.dims().len() as i64;
1164 let actual_dim = if dim < 0 { ndims + dim } else { dim } as usize;
1165
1166 let dim_size = x.dims()[actual_dim];
1168 if k > dim_size {
1169 return Err(anyhow::anyhow!("k ({}) is larger than dimension size ({})", k, dim_size));
1170 }
1171
1172 if x.dims().len() == 2 && actual_dim == 1 {
1175 let (batch, _n) = (x.dims()[0], x.dims()[1]);
1176 let mut all_values = Vec::with_capacity(batch * k);
1177 let mut all_indices = Vec::with_capacity(batch * k);
1178
1179 for b in 0..batch {
1180 let row = x.get(b)?;
1181 let row_data: Vec<f32> = row.to_vec1()?;
1182
1183 let mut indexed: Vec<(usize, f32)> = row_data.iter().cloned().enumerate().collect();
1185 indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
1186
1187 for i in 0..k {
1189 all_values.push(indexed[i].1);
1190 all_indices.push(indexed[i].0 as i64);
1191 }
1192 }
1193
1194 let device = input.device();
1195 let values = Tensor::from_f32_slice(&all_values, &[batch, k], device)?;
1196 let indices = Tensor::from_i64_slice(&all_indices, &[batch, k], device)?;
1197
1198 Ok((values, indices))
1199 } else if x.dims().len() == 1 {
1200 let data: Vec<f32> = x.to_vec1()?;
1202 let mut indexed: Vec<(usize, f32)> = data.iter().cloned().enumerate().collect();
1203 indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
1204
1205 let values: Vec<f32> = indexed.iter().take(k).map(|(_, v)| *v).collect();
1206 let indices: Vec<i64> = indexed.iter().take(k).map(|(i, _)| *i as i64).collect();
1207
1208 let device = input.device();
1209 let values_tensor = Tensor::from_f32_slice(&values, &[k], device)?;
1210 let indices_tensor = Tensor::from_i64_slice(&indices, &[k], device)?;
1211
1212 Ok((values_tensor, indices_tensor))
1213 } else if x.dims().len() == 3 && actual_dim == 2 {
1214 let (batch, seq_len, _n) = (x.dims()[0], x.dims()[1], x.dims()[2]);
1216 let mut all_values = Vec::with_capacity(batch * seq_len * k);
1217 let mut all_indices = Vec::with_capacity(batch * seq_len * k);
1218
1219 for b in 0..batch {
1220 for s in 0..seq_len {
1221 let row = x.get(b)?.get(s)?;
1222 let row_data: Vec<f32> = row.to_vec1()?;
1223
1224 let mut indexed: Vec<(usize, f32)> = row_data.iter().cloned().enumerate().collect();
1225 indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
1226
1227 for i in 0..k {
1228 all_values.push(indexed[i].1);
1229 all_indices.push(indexed[i].0 as i64);
1230 }
1231 }
1232 }
1233
1234 let device = input.device();
1235 let values = Tensor::from_f32_slice(&all_values, &[batch, seq_len, k], device)?;
1236 let indices = Tensor::from_i64_slice(&all_indices, &[batch, seq_len, k], device)?;
1237
1238 Ok((values, indices))
1239 } else {
1240 Err(anyhow::anyhow!("topk not implemented for tensor with {} dimensions on dim {}", x.dims().len(), actual_dim))
1241 }
1242 }
1243
1244 fn conv1d(&self, input: &Tensor, weight: &Tensor, bias: Option<&Tensor>, stride: usize, padding: usize) -> Result<Tensor> {
1245 let x = input.to_candle()?;
1246 let w = weight.to_candle()?;
1247
1248 let x_padded = if padding > 0 {
1253 x.pad_with_zeros(2, padding, padding)?
1254 } else {
1255 x
1256 };
1257
1258 let result = x_padded.conv1d(&w, padding, stride, 1, 1)?;
1260
1261 let result = if let Some(b) = bias {
1263 let b_candle = b.to_candle()?;
1264 let b_expanded = b_candle.unsqueeze(0)?.unsqueeze(2)?;
1266 result.broadcast_add(&b_expanded)?
1267 } else {
1268 result
1269 };
1270
1271 Ok(Tensor::from_candle(result))
1272 }
1273
1274 fn tanh(&self, input: &Tensor) -> Result<Tensor> {
1275 let x = input.to_candle()?;
1276 let result = x.tanh()?;
1277 Ok(Tensor::from_candle(result))
1278 }
1279
1280 fn sub(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
1281 let a_candle = a.to_candle()?;
1282 let b_candle = b.to_candle()?;
1283
1284 let result = if a.shape() == b.shape() {
1285 (&a_candle - &b_candle)?
1286 } else {
1287 a_candle.broadcast_sub(&b_candle)?
1288 };
1289
1290 Ok(Tensor::from_candle(result))
1291 }
1292
1293 fn clamp(&self, input: &Tensor, min: f32, max: f32) -> Result<Tensor> {
1294 let x = input.to_candle()?;
1295 let result = x.clamp(min as f64, max as f64)?;
1296 Ok(Tensor::from_candle(result))
1297 }
1298
1299 fn gather(&self, input: &Tensor, dim: usize, indices: &Tensor) -> Result<Tensor> {
1300 let x = input.to_candle()?;
1301 let idx = indices.to_candle()?;
1302 let idx_u32 = idx.to_dtype(DType::U32)?;
1303 let result = x.gather(&idx_u32, dim)?;
1304 Ok(Tensor::from_candle(result))
1305 }
1306
1307 fn scatter(&self, input: &Tensor, dim: usize, indices: &Tensor, src: &Tensor) -> Result<Tensor> {
1308 let x = input.to_candle()?;
1309 let idx = indices.to_candle()?;
1310 let s = src.to_candle()?;
1311 let idx_u32 = idx.to_dtype(DType::U32)?;
1312 let result = x.scatter_add(&idx_u32, &s, dim)?;
1313 Ok(Tensor::from_candle(result))
1314 }
1315
1316 fn flash_attention(
1319 &self,
1320 query: &Tensor,
1321 key: &Tensor,
1322 value: &Tensor,
1323 scale: f32,
1324 causal: bool,
1325 ) -> Result<Tensor> {
1326 let q = query.to_candle()?;
1329 let k = key.to_candle()?;
1330 let v = value.to_candle()?;
1331
1332 let q_dims = q.dims();
1334 let k_dims = k.dims();
1335
1336 let seq_q = q_dims.get(2).copied().unwrap_or(1);
1337 let seq_k = k_dims.get(2).copied().unwrap_or(1);
1338
1339 let k_t = k.transpose(k_dims.len() - 2, k_dims.len() - 1)?;
1341 let scores = q.matmul(&k_t)?;
1342
1343 let scaled_scores = (scores * scale as f64)?;
1345
1346 let masked_scores = if causal && seq_q > 1 {
1348 let mask_device = scaled_scores.device();
1351 let mut mask_data = vec![0.0f32; seq_q * seq_k];
1352 for i in 0..seq_q {
1353 for j in 0..seq_k {
1354 if j > i {
1355 mask_data[i * seq_k + j] = f32::NEG_INFINITY;
1356 }
1357 }
1358 }
1359 let mask = CandleTensor::from_slice(&mask_data, &[seq_q, seq_k], mask_device)?;
1360
1361 let scores_shape = scaled_scores.dims();
1363 let mask_shape: Vec<usize> = scores_shape.iter()
1364 .take(scores_shape.len() - 2)
1365 .map(|_| 1)
1366 .chain([seq_q, seq_k])
1367 .collect();
1368 let mask_reshaped = mask.reshape(mask_shape.as_slice())?;
1369 let mask_broadcast = mask_reshaped.broadcast_as(scores_shape)?;
1370
1371 (&scaled_scores + &mask_broadcast)?
1372 } else {
1373 scaled_scores
1374 };
1375
1376 let attention_weights = candle_ops::softmax_last_dim(&masked_scores)?;
1378
1379 let result = attention_weights.matmul(&v)?;
1381
1382 Ok(Tensor::from_candle(result))
1383 }
1384
1385 fn fused_swiglu(&self, gate: &Tensor, up: &Tensor) -> Result<Tensor> {
1386 let g = gate.to_candle()?;
1389 let u = up.to_candle()?;
1390
1391 let silu_gate = g.silu()?;
1393 let result = (&silu_gate * &u)?;
1394
1395 Ok(Tensor::from_candle(result))
1396 }
1397
1398 fn fused_residual_rms_norm(
1399 &self,
1400 residual: &Tensor,
1401 hidden: &Tensor,
1402 weight: &Tensor,
1403 eps: f32,
1404 ) -> Result<Tensor> {
1405 let r = residual.to_candle()?;
1408 let h = hidden.to_candle()?;
1409 let w = weight.to_candle()?;
1410
1411 let combined = (&r + &h)?;
1413
1414 let last_dim = combined.dims().len() - 1;
1416 let x_squared = combined.sqr()?;
1417 let mean_squared = x_squared.mean_keepdim(last_dim)?;
1418 let rms = (mean_squared + eps as f64)?.sqrt()?;
1419 let normalized = combined.broadcast_div(&rms)?;
1420 let result = normalized.broadcast_mul(&w)?;
1421
1422 Ok(Tensor::from_candle(result))
1423 }
1424}
1425
1426impl DataType {
1427 pub fn size_bytes(&self) -> usize {
1428 match self {
1429 DataType::Float32 => 4,
1430 DataType::Float16 => 2,
1431 DataType::BFloat16 => 2,
1432 DataType::Int32 => 4,
1433 DataType::Int64 => 8,
1434 DataType::Int8 => 1,
1435 DataType::Bool => 1,
1436 }
1437 }
1438
1439 pub fn to_candle(&self) -> DType {
1441 match self {
1442 DataType::Float32 => DType::F32,
1443 DataType::Float16 => DType::F16,
1444 DataType::BFloat16 => DType::BF16,
1445 DataType::Int32 => DType::U32, DataType::Int64 => DType::I64,
1447 DataType::Int8 => DType::U8, DataType::Bool => DType::U8, }
1450 }
1451
1452 pub fn from_candle(dtype: DType) -> Self {
1454 match dtype {
1455 DType::F32 => DataType::Float32,
1456 DType::F16 => DataType::Float16,
1457 DType::BF16 => DataType::BFloat16,
1458 DType::I64 => DataType::Int64,
1459 DType::U8 => DataType::Int8,
1460 DType::F64 => DataType::Float32, DType::U32 => DataType::Int32, }
1463 }
1464}
1465
1466impl Device {
1467 pub fn to_candle(&self) -> CandleDevice {
1469 match self {
1470 Device::CPU => CandleDevice::Cpu,
1471 Device::CUDA(id) => CandleDevice::cuda_if_available(*id).unwrap_or(CandleDevice::Cpu),
1472 Device::Metal(id) => {
1473 #[cfg(feature = "metal")]
1474 {
1475 CandleDevice::new_metal(*id).unwrap_or(CandleDevice::Cpu)
1476 }
1477 #[cfg(not(feature = "metal"))]
1478 {
1479 let _ = id;
1480 CandleDevice::Cpu
1481 }
1482 }
1483 }
1484 }
1485
1486 pub fn from_candle(device: &CandleDevice) -> Self {
1488 match device {
1489 CandleDevice::Cpu => Device::CPU,
1490 CandleDevice::Cuda(_) => Device::CUDA(0), CandleDevice::Metal(_) => Device::Metal(0), }
1493 }
1494}
1495
1496static TENSOR_OPS: std::sync::OnceLock<TensorDispatcher> = std::sync::OnceLock::new();
1498
1499pub fn ops() -> &'static TensorDispatcher {
1501 TENSOR_OPS.get_or_init(|| TensorDispatcher::new())
1502}
1503
1504impl TensorOps for GpuTensorOpsImpl {
1507 fn matmul(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
1508 CpuTensorOpsImpl.matmul(a, b)
1510 }
1511
1512 fn add(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
1513 CpuTensorOpsImpl.add(a, b)
1514 }
1515
1516 fn mul(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
1517 CpuTensorOpsImpl.mul(a, b)
1518 }
1519
1520 fn attention(
1521 &self,
1522 query: &Tensor,
1523 key: &Tensor,
1524 value: &Tensor,
1525 mask: Option<&Tensor>,
1526 scale: Option<f32>,
1527 ) -> Result<Tensor> {
1528 CpuTensorOpsImpl.attention(query, key, value, mask, scale)
1529 }
1530
1531 fn layer_norm(
1532 &self,
1533 input: &Tensor,
1534 weight: &Tensor,
1535 bias: Option<&Tensor>,
1536 eps: f32,
1537 ) -> Result<Tensor> {
1538 CpuTensorOpsImpl.layer_norm(input, weight, bias, eps)
1539 }
1540
1541 fn gelu(&self, input: &Tensor) -> Result<Tensor> {
1542 CpuTensorOpsImpl.gelu(input)
1543 }
1544
1545 fn silu(&self, input: &Tensor) -> Result<Tensor> {
1546 CpuTensorOpsImpl.silu(input)
1547 }
1548
1549 fn softmax(&self, input: &Tensor, dim: isize) -> Result<Tensor> {
1550 CpuTensorOpsImpl.softmax(input, dim)
1551 }
1552
1553 fn embedding(&self, indices: &Tensor, weight: &Tensor) -> Result<Tensor> {
1554 CpuTensorOpsImpl.embedding(indices, weight)
1555 }
1556
1557 fn zeros(&self, shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor> {
1558 CpuTensorOpsImpl.zeros(shape, dtype, device)
1559 }
1560
1561 fn randn(&self, shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor> {
1562 CpuTensorOpsImpl.randn(shape, dtype, device)
1563 }
1564
1565 fn exp(&self, input: &Tensor) -> Result<Tensor> {
1566 CpuTensorOpsImpl.exp(input)
1567 }
1568
1569 fn normalize(&self, input: &Tensor, p: i32, dim: i32) -> Result<Tensor> {
1570 CpuTensorOpsImpl.normalize(input, p, dim)
1571 }
1572
1573 fn concat(&self, tensors: &[&Tensor], dim: usize) -> Result<Tensor> {
1574 CpuTensorOpsImpl.concat(tensors, dim)
1575 }
1576
1577 fn rms_norm(&self, input: &Tensor, weight: &Tensor, eps: f32) -> Result<Tensor> {
1578 CpuTensorOpsImpl.rms_norm(input, weight, eps)
1579 }
1580
1581 fn scale(&self, input: &Tensor, factor: f32) -> Result<Tensor> {
1582 CpuTensorOpsImpl.scale(input, factor)
1583 }
1584
1585 fn sigmoid(&self, input: &Tensor) -> Result<Tensor> {
1586 CpuTensorOpsImpl.sigmoid(input)
1587 }
1588
1589 fn topk(&self, input: &Tensor, k: usize, dim: i64) -> Result<(Tensor, Tensor)> {
1590 CpuTensorOpsImpl.topk(input, k, dim)
1591 }
1592
1593 fn conv1d(&self, input: &Tensor, weight: &Tensor, bias: Option<&Tensor>, stride: usize, padding: usize) -> Result<Tensor> {
1594 CpuTensorOpsImpl.conv1d(input, weight, bias, stride, padding)
1595 }
1596
1597 fn tanh(&self, input: &Tensor) -> Result<Tensor> {
1598 CpuTensorOpsImpl.tanh(input)
1599 }
1600
1601 fn sub(&self, a: &Tensor, b: &Tensor) -> Result<Tensor> {
1602 CpuTensorOpsImpl.sub(a, b)
1603 }
1604
1605 fn clamp(&self, input: &Tensor, min: f32, max: f32) -> Result<Tensor> {
1606 CpuTensorOpsImpl.clamp(input, min, max)
1607 }
1608
1609 fn gather(&self, input: &Tensor, dim: usize, indices: &Tensor) -> Result<Tensor> {
1610 CpuTensorOpsImpl.gather(input, dim, indices)
1611 }
1612
1613 fn scatter(&self, input: &Tensor, dim: usize, indices: &Tensor, src: &Tensor) -> Result<Tensor> {
1614 CpuTensorOpsImpl.scatter(input, dim, indices, src)
1615 }
1616
1617 fn flash_attention(&self, query: &Tensor, key: &Tensor, value: &Tensor, scale: f32, causal: bool) -> Result<Tensor> {
1619 CpuTensorOpsImpl.flash_attention(query, key, value, scale, causal)
1620 }
1621
1622 fn fused_swiglu(&self, gate: &Tensor, up: &Tensor) -> Result<Tensor> {
1623 CpuTensorOpsImpl.fused_swiglu(gate, up)
1624 }
1625
1626 fn fused_residual_rms_norm(&self, residual: &Tensor, hidden: &Tensor, weight: &Tensor, eps: f32) -> Result<Tensor> {
1627 CpuTensorOpsImpl.fused_residual_rms_norm(residual, hidden, weight, eps)
1628 }
1629}
1630
1631pub mod ops_fn {
1633 use super::*;
1634
1635 pub fn matmul(a: &Tensor, b: &Tensor) -> Result<Tensor> {
1636 ops().matmul(a, b)
1637 }
1638
1639 pub fn add(a: &Tensor, b: &Tensor) -> Result<Tensor> {
1640 ops().add(a, b)
1641 }
1642
1643 pub fn zeros(shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor> {
1644 ops().zeros(shape, dtype, device)
1645 }
1646
1647 pub fn randn(shape: &[usize], dtype: DataType, device: &Device) -> Result<Tensor> {
1648 ops().randn(shape, dtype, device)
1649 }
1650
1651 pub fn layer_norm(input: &Tensor, weight: &Tensor, bias: Option<&Tensor>, eps: f32) -> Result<Tensor> {
1652 ops().layer_norm(input, weight, bias, eps)
1653 }
1654
1655 pub fn embedding(indices: &Tensor, weight: &Tensor) -> Result<Tensor> {
1656 ops().embedding(indices, weight)
1657 }
1658
1659 pub fn silu(input: &Tensor) -> Result<Tensor> {
1660 ops().silu(input)
1661 }
1662
1663 pub fn mul(a: &Tensor, b: &Tensor) -> Result<Tensor> {
1664 ops().mul(a, b)
1665 }
1666
1667 pub fn attention(query: &Tensor, key: &Tensor, value: &Tensor, mask: Option<&Tensor>) -> Result<Tensor> {
1668 ops().attention(query, key, value, mask, None)
1669 }
1670
1671 pub fn gelu(input: &Tensor) -> Result<Tensor> {
1672 ops().gelu(input)
1673 }
1674
1675 pub fn exp(input: &Tensor) -> Result<Tensor> {
1676 ops().exp(input)
1677 }
1678
1679 pub fn normalize(input: &Tensor, p: i32, dim: i32) -> Result<Tensor> {
1680 ops().normalize(input, p, dim)
1681 }
1682
1683 pub fn concat(tensors: &[&Tensor], dim: usize) -> Result<Tensor> {
1684 ops().concat(tensors, dim)
1685 }
1686
1687 pub fn rms_norm(input: &Tensor, weight: &Tensor, eps: f32) -> Result<Tensor> {
1688 ops().rms_norm(input, weight, eps)
1689 }
1690
1691 pub fn scale(input: &Tensor, factor: f32) -> Result<Tensor> {
1692 ops().scale(input, factor)
1693 }
1694
1695 pub fn transpose(input: &Tensor) -> Result<Tensor> {
1697 input.transpose()
1698 }
1699
1700 pub fn transpose_dims(input: &Tensor, dim0: usize, dim1: usize) -> Result<Tensor> {
1702 input.transpose_dims(dim0, dim1)
1703 }
1704
1705 pub fn sigmoid(input: &Tensor) -> Result<Tensor> {
1707 ops().sigmoid(input)
1708 }
1709
1710 pub fn topk(input: &Tensor, k: usize, dim: i64) -> Result<(Tensor, Tensor)> {
1712 ops().topk(input, k, dim)
1713 }
1714
1715 pub fn conv1d(input: &Tensor, weight: &Tensor, bias: Option<&Tensor>, stride: usize, padding: usize) -> Result<Tensor> {
1717 ops().conv1d(input, weight, bias, stride, padding)
1718 }
1719
1720 pub fn tanh(input: &Tensor) -> Result<Tensor> {
1722 ops().tanh(input)
1723 }
1724
1725 pub fn sub(a: &Tensor, b: &Tensor) -> Result<Tensor> {
1727 ops().sub(a, b)
1728 }
1729
1730 pub fn clamp(input: &Tensor, min: f32, max: f32) -> Result<Tensor> {
1732 ops().clamp(input, min, max)
1733 }
1734
1735 pub fn gather(input: &Tensor, dim: usize, indices: &Tensor) -> Result<Tensor> {
1737 ops().gather(input, dim, indices)
1738 }
1739
1740 pub fn scatter(input: &Tensor, dim: usize, indices: &Tensor, src: &Tensor) -> Result<Tensor> {
1742 ops().scatter(input, dim, indices, src)
1743 }
1744
1745 pub fn softmax(input: &Tensor, dim: isize) -> Result<Tensor> {
1747 ops().softmax(input, dim)
1748 }
1749
1750 pub fn causal_mask(seq_len: usize, device: &Device) -> Result<Tensor> {
1752 let mut mask_data = vec![0.0f32; seq_len * seq_len];
1753 for i in 0..seq_len {
1754 for j in 0..=i {
1755 mask_data[i * seq_len + j] = 1.0;
1756 }
1757 }
1758 Tensor::from_f32_slice(&mask_data, &[seq_len, seq_len], device)
1759 }
1760
1761 pub fn sliding_window_mask(seq_len: usize, window_size: usize, device: &Device) -> Result<Tensor> {
1765 let mut mask_data = vec![0.0f32; seq_len * seq_len];
1766 for i in 0..seq_len {
1767 let start = if i >= window_size { i - window_size + 1 } else { 0 };
1769 for j in start..=i {
1770 mask_data[i * seq_len + j] = 1.0;
1771 }
1772 }
1773 Tensor::from_f32_slice(&mask_data, &[seq_len, seq_len], device)
1774 }
1775
1776 pub fn causal_sliding_window_mask(seq_len: usize, window_size: usize, device: &Device) -> Result<Tensor> {
1779 sliding_window_mask(seq_len, window_size, device)
1781 }
1782
1783 pub fn flash_attention(
1798 query: &Tensor,
1799 key: &Tensor,
1800 value: &Tensor,
1801 scale: f32,
1802 causal: bool,
1803 ) -> Result<Tensor> {
1804 ops().flash_attention(query, key, value, scale, causal)
1805 }
1806
1807 pub fn fused_swiglu(gate: &Tensor, up: &Tensor) -> Result<Tensor> {
1810 ops().fused_swiglu(gate, up)
1811 }
1812
1813 pub fn fused_residual_rms_norm(
1817 residual: &Tensor,
1818 hidden: &Tensor,
1819 weight: &Tensor,
1820 eps: f32,
1821 ) -> Result<Tensor> {
1822 ops().fused_residual_rms_norm(residual, hidden, weight, eps)
1823 }
1824}