1use ruda_autodiff::{Autodiff, AutodiffValue};
2use ruda_tensor::{Backend, AutodiffContext, TensorMetadata, QTensorPrimitive, DeviceOps, DType, Shape};
3use ruda_tensor::ops::*;
4use ruda_tensor::tensor::{FloatTensor, IntTensor, BoolTensor, QuantizedTensor};
5use ruda_core::tensor::{QuantScheme, QuantAcc, QuantPropagation};
6use crate::{DispatchDevice, backends};
7
8#[derive(Clone, Debug)]
9pub enum BackendTensor<B: Backend> {
10Float(FloatTensor<B>),
11AdFloat(FloatTensor<Autodiff<B>>),
12Int(IntTensor<B>),
13AdInt(IntTensor<Autodiff<B>>),
14Bool(BoolTensor<B>),
15AdBool(BoolTensor<Autodiff<B>>),
16Quantized(QuantizedTensor<B>),
17AdQuantized(QuantizedTensor<Autodiff<B>>),
18}
19
20impl<B: Backend> BackendTensor<B> {
21pub(crate) fn into_native_float(self, _: AutodiffContext) -> FloatTensor<B> {
22 match self {
23 Self::Float(tensor) => tensor,
24 Self::AdFloat(tensor) => tensor.primitive,
25 _ => panic!("Expected a float tensor"),
26 }
27}
28pub(crate) fn into_ad_float(self, context: AutodiffContext) -> FloatTensor<Autodiff<B>> {
29 let tensor = match self {
30 Self::AdFloat(tensor) => tensor,
31 Self::Float(tensor) => <FloatTensor<Autodiff<B>>>::new(tensor),
32 _ => panic!("Expected a float tensor"),
33 };
34 Autodiff::<B>::float_with_autodiff_context(tensor, context)
35}
36pub(crate) fn into_native_int(self, _: AutodiffContext) -> IntTensor<B> {
37 match self {
38 Self::Int(tensor) => tensor,
39 Self::AdInt(tensor) => tensor.primitive,
40 _ => panic!("Expected a int tensor"),
41 }
42}
43pub(crate) fn into_ad_int(self, context: AutodiffContext) -> IntTensor<Autodiff<B>> {
44 let tensor = match self {
45 Self::AdInt(tensor) => tensor,
46 Self::Int(tensor) => AutodiffValue::with_context(tensor, context),
47 _ => panic!("Expected a int tensor"),
48 };
49 Autodiff::<B>::int_with_autodiff_context(tensor, context)
50}
51pub(crate) fn into_native_bool(self, _: AutodiffContext) -> BoolTensor<B> {
52 match self {
53 Self::Bool(tensor) => tensor,
54 Self::AdBool(tensor) => tensor.primitive,
55 _ => panic!("Expected a bool tensor"),
56 }
57}
58pub(crate) fn into_ad_bool(self, context: AutodiffContext) -> BoolTensor<Autodiff<B>> {
59 let tensor = match self {
60 Self::AdBool(tensor) => tensor,
61 Self::Bool(tensor) => AutodiffValue::with_context(tensor, context),
62 _ => panic!("Expected a bool tensor"),
63 };
64 Autodiff::<B>::bool_with_autodiff_context(tensor, context)
65}
66pub(crate) fn into_native_quantized(self, _: AutodiffContext) -> QuantizedTensor<B> {
67 match self {
68 Self::Quantized(tensor) => tensor,
69 Self::AdQuantized(tensor) => tensor.primitive,
70 _ => panic!("Expected a quantized tensor"),
71 }
72}
73pub(crate) fn into_ad_quantized(self, context: AutodiffContext) -> QuantizedTensor<Autodiff<B>> {
74 let tensor = match self {
75 Self::AdQuantized(tensor) => tensor,
76 Self::Quantized(tensor) => ruda_autodiff::AutodiffQTensor::untracked(tensor),
77 _ => panic!("Expected a quantized tensor"),
78 };
79 Autodiff::<B>::q_with_autodiff_context(tensor, context)
80}
81
82 pub fn context(&self) -> AutodiffContext {
83 match self {
84Self::AdFloat(tensor) => Autodiff::<B>::float_autodiff_context(tensor),
85Self::AdInt(tensor) => Autodiff::<B>::int_autodiff_context(tensor),
86Self::AdBool(tensor) => Autodiff::<B>::bool_autodiff_context(tensor),
87Self::AdQuantized(tensor) => Autodiff::<B>::q_autodiff_context(tensor),
88 _ => AutodiffContext::Disabled,
89 }
90 }
91
92 pub fn with_context(self, context: AutodiffContext) -> Self {
93 match context {
94 AutodiffContext::Disabled => match self {
95value @ (Self::Float(_) | Self::AdFloat(_)) => Self::Float(value.into_native_float(context)),
96value @ (Self::Int(_) | Self::AdInt(_)) => Self::Int(value.into_native_int(context)),
97value @ (Self::Bool(_) | Self::AdBool(_)) => Self::Bool(value.into_native_bool(context)),
98value @ (Self::Quantized(_) | Self::AdQuantized(_)) => Self::Quantized(value.into_native_quantized(context)),
99 },
100 AutodiffContext::Enabled(_) => match self {
101value @ (Self::Float(_) | Self::AdFloat(_)) => Self::AdFloat(value.into_ad_float(context)),
102value @ (Self::Int(_) | Self::AdInt(_)) => Self::AdInt(value.into_ad_int(context)),
103value @ (Self::Bool(_) | Self::AdBool(_)) => Self::AdBool(value.into_ad_bool(context)),
104value @ (Self::Quantized(_) | Self::AdQuantized(_)) => Self::AdQuantized(value.into_ad_quantized(context)),
105 },
106 }
107 }
108
109 pub fn device(&self) -> B::Device {
110 match self {
111Self::Float(tensor) => B::float_device(tensor),
112Self::AdFloat(tensor) => B::float_device(&tensor.primitive),
113Self::Int(tensor) => B::int_device(tensor),
114Self::AdInt(tensor) => B::int_device(&tensor.primitive),
115Self::Bool(tensor) => B::bool_device(tensor),
116Self::AdBool(tensor) => B::bool_device(&tensor.primitive),
117Self::Quantized(tensor) => B::q_device(tensor),
118Self::AdQuantized(tensor) => B::q_device(&tensor.primitive),
119 }
120 }
121}
122
123impl<B: Backend> TensorMetadata for BackendTensor<B> {
124fn dtype(&self) -> DType {
125 match self {
126Self::Float(tensor) => tensor.dtype(),
127Self::AdFloat(tensor) => tensor.dtype(),
128Self::Int(tensor) => tensor.dtype(),
129Self::AdInt(tensor) => tensor.dtype(),
130Self::Bool(tensor) => tensor.dtype(),
131Self::AdBool(tensor) => tensor.dtype(),
132Self::Quantized(tensor) => tensor.dtype(),
133Self::AdQuantized(tensor) => tensor.dtype(),
134 }
135}
136fn shape(&self) -> Shape {
137 match self {
138Self::Float(tensor) => tensor.shape(),
139Self::AdFloat(tensor) => tensor.shape(),
140Self::Int(tensor) => tensor.shape(),
141Self::AdInt(tensor) => tensor.shape(),
142Self::Bool(tensor) => tensor.shape(),
143Self::AdBool(tensor) => tensor.shape(),
144Self::Quantized(tensor) => tensor.shape(),
145Self::AdQuantized(tensor) => tensor.shape(),
146 }
147}
148fn rank(&self) -> usize {
149 match self {
150Self::Float(tensor) => tensor.rank(),
151Self::AdFloat(tensor) => tensor.rank(),
152Self::Int(tensor) => tensor.rank(),
153Self::AdInt(tensor) => tensor.rank(),
154Self::Bool(tensor) => tensor.rank(),
155Self::AdBool(tensor) => tensor.rank(),
156Self::Quantized(tensor) => tensor.rank(),
157Self::AdQuantized(tensor) => tensor.rank(),
158 }
159}
160fn can_mut(&self) -> bool {
161 match self {
162Self::Float(tensor) => tensor.can_mut(),
163Self::AdFloat(tensor) => tensor.can_mut(),
164Self::Int(tensor) => tensor.can_mut(),
165Self::AdInt(tensor) => tensor.can_mut(),
166Self::Bool(tensor) => tensor.can_mut(),
167Self::AdBool(tensor) => tensor.can_mut(),
168Self::Quantized(tensor) => tensor.can_mut(),
169Self::AdQuantized(tensor) => tensor.can_mut(),
170 }
171}
172}
173
174impl<B: Backend> QTensorPrimitive for BackendTensor<B> {
175fn scheme(&self) -> &QuantScheme {
176 match self {
177 Self::Quantized(tensor) => tensor.scheme(),
178 Self::AdQuantized(tensor) => tensor.primitive.scheme(),
179 _ => panic!("Expected a quantized tensor"),
180 }
181}
182fn acc_precision(&self) -> QuantAcc {
183 match self {
184 Self::Quantized(tensor) => tensor.acc_precision(),
185 Self::AdQuantized(tensor) => tensor.primitive.acc_precision(),
186 _ => panic!("Expected a quantized tensor"),
187 }
188}
189fn propagation(&self) -> QuantPropagation {
190 match self {
191 Self::Quantized(tensor) => tensor.propagation(),
192 Self::AdQuantized(tensor) => tensor.primitive.propagation(),
193 _ => panic!("Expected a quantized tensor"),
194 }
195}
196}
197
198#[derive(Clone, Debug)]
199pub enum DispatchTensor {
200Host(BackendTensor<backends::Host>),
201#[cfg(feature = "cuda")]
202Cuda(BackendTensor<backends::Cuda>),
203#[cfg(feature = "cpu")]
204Cpu(BackendTensor<backends::Cpu>),
205#[cfg(feature = "wgpu")]
206Wgpu(BackendTensor<backends::Wgpu>),
207#[cfg(feature = "rocm")]
208Rocm(BackendTensor<backends::Rocm>),
209#[cfg(feature = "tch")]
210LibTorch(BackendTensor<backends::LibTorch>),
211#[cfg(feature = "remote")]
212Remote(BackendTensor<backends::Remote>),
213#[cfg(feature = "capture")]
214Capture(BackendTensor<backends::Capture>),
215}
216
217impl DispatchTensor {
218 #[cfg(feature = "capture")]
220 pub fn capture_tensor_id(&self) -> Option<ruda_tensor_capture::TensorId> {
221 match self {
222 Self::Capture(BackendTensor::Float(tensor) | BackendTensor::Int(tensor)
223 | BackendTensor::Bool(tensor) | BackendTensor::Quantized(tensor)) => Some(tensor.id()),
224 _ => None,
225 }
226 }
227
228 pub fn context(&self) -> AutodiffContext {
229 match self {
230Self::Host(tensor) => tensor.context(),
231#[cfg(feature = "cuda")]
232Self::Cuda(tensor) => tensor.context(),
233#[cfg(feature = "cpu")]
234Self::Cpu(tensor) => tensor.context(),
235#[cfg(feature = "wgpu")]
236Self::Wgpu(tensor) => tensor.context(),
237#[cfg(feature = "rocm")]
238Self::Rocm(tensor) => tensor.context(),
239#[cfg(feature = "tch")]
240Self::LibTorch(tensor) => tensor.context(),
241#[cfg(feature = "remote")]
242Self::Remote(tensor) => tensor.context(),
243#[cfg(feature = "capture")]
244Self::Capture(tensor) => tensor.context(),
245 }
246 }
247
248 pub fn with_context(self, context: AutodiffContext) -> Self {
249 match self {
250Self::Host(tensor) => Self::Host(tensor.with_context(context)),
251#[cfg(feature = "cuda")]
252Self::Cuda(tensor) => Self::Cuda(tensor.with_context(context)),
253#[cfg(feature = "cpu")]
254Self::Cpu(tensor) => Self::Cpu(tensor.with_context(context)),
255#[cfg(feature = "wgpu")]
256Self::Wgpu(tensor) => Self::Wgpu(tensor.with_context(context)),
257#[cfg(feature = "rocm")]
258Self::Rocm(tensor) => Self::Rocm(tensor.with_context(context)),
259#[cfg(feature = "tch")]
260Self::LibTorch(tensor) => Self::LibTorch(tensor.with_context(context)),
261#[cfg(feature = "remote")]
262Self::Remote(tensor) => Self::Remote(tensor.with_context(context)),
263#[cfg(feature = "capture")]
264Self::Capture(tensor) => {
265 assert_eq!(context, AutodiffContext::Disabled, "Capture does not support autodiff");
266 Self::Capture(tensor.with_context(context))
267},
268 }
269 }
270
271 pub fn device(&self) -> DispatchDevice {
272 let device = match self {
273Self::Host(tensor) => DispatchDevice::Host(tensor.device()),
274#[cfg(feature = "cuda")]
275Self::Cuda(tensor) => DispatchDevice::Cuda(tensor.device()),
276#[cfg(feature = "cpu")]
277Self::Cpu(tensor) => DispatchDevice::Cpu(tensor.device()),
278#[cfg(feature = "wgpu")]
279Self::Wgpu(tensor) => DispatchDevice::Wgpu(tensor.device()),
280#[cfg(feature = "rocm")]
281Self::Rocm(tensor) => DispatchDevice::Rocm(tensor.device()),
282#[cfg(feature = "tch")]
283Self::LibTorch(tensor) => DispatchDevice::LibTorch(tensor.device()),
284#[cfg(feature = "remote")]
285Self::Remote(tensor) => DispatchDevice::Remote(tensor.device()),
286#[cfg(feature = "capture")]
287Self::Capture(tensor) => DispatchDevice::Capture(tensor.device()),
288 };
289 device.with_context(self.context())
290 }
291}
292
293impl TensorMetadata for DispatchTensor {
294fn dtype(&self) -> DType {
295 match self {
296Self::Host(tensor) => tensor.dtype(),
297#[cfg(feature = "cuda")]
298Self::Cuda(tensor) => tensor.dtype(),
299#[cfg(feature = "cpu")]
300Self::Cpu(tensor) => tensor.dtype(),
301#[cfg(feature = "wgpu")]
302Self::Wgpu(tensor) => tensor.dtype(),
303#[cfg(feature = "rocm")]
304Self::Rocm(tensor) => tensor.dtype(),
305#[cfg(feature = "tch")]
306Self::LibTorch(tensor) => tensor.dtype(),
307#[cfg(feature = "remote")]
308Self::Remote(tensor) => tensor.dtype(),
309#[cfg(feature = "capture")]
310Self::Capture(tensor) => tensor.dtype(),
311 }
312}
313fn shape(&self) -> Shape {
314 match self {
315Self::Host(tensor) => tensor.shape(),
316#[cfg(feature = "cuda")]
317Self::Cuda(tensor) => tensor.shape(),
318#[cfg(feature = "cpu")]
319Self::Cpu(tensor) => tensor.shape(),
320#[cfg(feature = "wgpu")]
321Self::Wgpu(tensor) => tensor.shape(),
322#[cfg(feature = "rocm")]
323Self::Rocm(tensor) => tensor.shape(),
324#[cfg(feature = "tch")]
325Self::LibTorch(tensor) => tensor.shape(),
326#[cfg(feature = "remote")]
327Self::Remote(tensor) => tensor.shape(),
328#[cfg(feature = "capture")]
329Self::Capture(tensor) => tensor.shape(),
330 }
331}
332fn rank(&self) -> usize {
333 match self {
334Self::Host(tensor) => tensor.rank(),
335#[cfg(feature = "cuda")]
336Self::Cuda(tensor) => tensor.rank(),
337#[cfg(feature = "cpu")]
338Self::Cpu(tensor) => tensor.rank(),
339#[cfg(feature = "wgpu")]
340Self::Wgpu(tensor) => tensor.rank(),
341#[cfg(feature = "rocm")]
342Self::Rocm(tensor) => tensor.rank(),
343#[cfg(feature = "tch")]
344Self::LibTorch(tensor) => tensor.rank(),
345#[cfg(feature = "remote")]
346Self::Remote(tensor) => tensor.rank(),
347#[cfg(feature = "capture")]
348Self::Capture(tensor) => tensor.rank(),
349 }
350}
351fn can_mut(&self) -> bool {
352 match self {
353Self::Host(tensor) => tensor.can_mut(),
354#[cfg(feature = "cuda")]
355Self::Cuda(tensor) => tensor.can_mut(),
356#[cfg(feature = "cpu")]
357Self::Cpu(tensor) => tensor.can_mut(),
358#[cfg(feature = "wgpu")]
359Self::Wgpu(tensor) => tensor.can_mut(),
360#[cfg(feature = "rocm")]
361Self::Rocm(tensor) => tensor.can_mut(),
362#[cfg(feature = "tch")]
363Self::LibTorch(tensor) => tensor.can_mut(),
364#[cfg(feature = "remote")]
365Self::Remote(tensor) => tensor.can_mut(),
366#[cfg(feature = "capture")]
367Self::Capture(tensor) => tensor.can_mut(),
368 }
369}
370}
371
372impl QTensorPrimitive for DispatchTensor {
373fn scheme(&self) -> &QuantScheme {
374 match self {
375Self::Host(tensor) => tensor.scheme(),
376#[cfg(feature = "cuda")]
377Self::Cuda(tensor) => tensor.scheme(),
378#[cfg(feature = "cpu")]
379Self::Cpu(tensor) => tensor.scheme(),
380#[cfg(feature = "wgpu")]
381Self::Wgpu(tensor) => tensor.scheme(),
382#[cfg(feature = "rocm")]
383Self::Rocm(tensor) => tensor.scheme(),
384#[cfg(feature = "tch")]
385Self::LibTorch(tensor) => tensor.scheme(),
386#[cfg(feature = "remote")]
387Self::Remote(tensor) => tensor.scheme(),
388#[cfg(feature = "capture")]
389Self::Capture(tensor) => tensor.scheme(),
390 }
391}
392fn acc_precision(&self) -> QuantAcc {
393 match self {
394Self::Host(tensor) => tensor.acc_precision(),
395#[cfg(feature = "cuda")]
396Self::Cuda(tensor) => tensor.acc_precision(),
397#[cfg(feature = "cpu")]
398Self::Cpu(tensor) => tensor.acc_precision(),
399#[cfg(feature = "wgpu")]
400Self::Wgpu(tensor) => tensor.acc_precision(),
401#[cfg(feature = "rocm")]
402Self::Rocm(tensor) => tensor.acc_precision(),
403#[cfg(feature = "tch")]
404Self::LibTorch(tensor) => tensor.acc_precision(),
405#[cfg(feature = "remote")]
406Self::Remote(tensor) => tensor.acc_precision(),
407#[cfg(feature = "capture")]
408Self::Capture(tensor) => tensor.acc_precision(),
409 }
410}
411fn propagation(&self) -> QuantPropagation {
412 match self {
413Self::Host(tensor) => tensor.propagation(),
414#[cfg(feature = "cuda")]
415Self::Cuda(tensor) => tensor.propagation(),
416#[cfg(feature = "cpu")]
417Self::Cpu(tensor) => tensor.propagation(),
418#[cfg(feature = "wgpu")]
419Self::Wgpu(tensor) => tensor.propagation(),
420#[cfg(feature = "rocm")]
421Self::Rocm(tensor) => tensor.propagation(),
422#[cfg(feature = "tch")]
423Self::LibTorch(tensor) => tensor.propagation(),
424#[cfg(feature = "remote")]
425Self::Remote(tensor) => tensor.propagation(),
426#[cfg(feature = "capture")]
427Self::Capture(tensor) => tensor.propagation(),
428 }
429}
430}