Skip to main content

burn_dispatch/ops/
qtensor.rs

1use burn_backend::{
2    DeviceOps, ExecutionError, FloatDType, Shape, Slice, TensorData, TensorMetadata,
3    TensorPrimitive,
4    ops::QTensorOps,
5    quantization::{QuantPropagation, QuantScheme, QuantizationParametersPrimitive},
6    tensor::{FloatTensor, IntTensor, QuantizedTensor},
7};
8
9use crate::{Dispatch, DispatchDevice};
10
11impl QTensorOps<Self> for Dispatch {
12    fn q_from_data(data: TensorData, device: &DispatchDevice) -> QuantizedTensor<Self> {
13        creation_op!(Quantized, device, |device| B::q_from_data(data, device))
14    }
15
16    fn quantize(
17        tensor: FloatTensor<Self>,
18        scheme: &QuantScheme,
19        qparams: QuantizationParametersPrimitive<Self>,
20    ) -> QuantizedTensor<Self> {
21        let QuantizationParametersPrimitive { scales, global } = qparams;
22        // On an autodiff device the tensor and its scales arrive autodiff-wrapped, and
23        // quantization detaches them: the packed result carries no graph.
24        multi_op!(
25            inputs[(tensor, float), (scales, float)],
26            opt_inputs[(global, float)],
27            => Quantized,
28            B::quantize(tensor, scheme, QuantizationParametersPrimitive { scales, global })
29        )
30    }
31
32    fn dequantize(tensor: QuantizedTensor<Self>, dtype: FloatDType) -> FloatTensor<Self> {
33        unary_op!(tensor, quantized, |tensor| B::dequantize(tensor, dtype) => Float)
34    }
35
36    fn q_to_device(
37        tensor: QuantizedTensor<Self>,
38        device: &DispatchDevice,
39    ) -> QuantizedTensor<Self> {
40        to_device!(
41            Quantized,
42            quantized,
43            tensor,
44            device,
45            q_to_device,
46            |inner, device| {
47                let data =
48                    burn_backend::read_sync(B1::q_into_data(inner)).expect("Should read data");
49                B2::q_from_data(data, device)
50            }
51        )
52    }
53
54    fn q_reshape(tensor: QuantizedTensor<Self>, shape: Shape) -> QuantizedTensor<Self> {
55        unary_op!(tensor, quantized, |tensor| B::q_reshape(tensor, shape) => Quantized)
56    }
57
58    async fn q_into_data(tensor: QuantizedTensor<Self>) -> Result<TensorData, ExecutionError> {
59        unary_op!(tensor, quantized, |tensor| B::q_into_data(tensor).await)
60    }
61
62    fn q_expand(tensor: QuantizedTensor<Self>, shape: Shape) -> QuantizedTensor<Self> {
63        unary_op!(tensor, quantized, |tensor| B::q_expand(tensor, shape) => Quantized)
64    }
65
66    fn q_swap_dims(
67        tensor: QuantizedTensor<Self>,
68        dim1: usize,
69        dim2: usize,
70    ) -> QuantizedTensor<Self> {
71        unary_op!(tensor, quantized, |tensor| B::q_swap_dims(tensor, dim1, dim2) => Quantized)
72    }
73
74    fn q_permute(tensor: QuantizedTensor<Self>, axes: &[usize]) -> QuantizedTensor<Self> {
75        unary_op!(tensor, quantized, |tensor| B::q_permute(tensor, axes) => Quantized)
76    }
77
78    fn q_flip(tensor: QuantizedTensor<Self>, axes: &[usize]) -> QuantizedTensor<Self> {
79        unary_op!(tensor, quantized, |tensor| B::q_flip(tensor, axes) => Quantized)
80    }
81
82    fn q_select(
83        tensor: QuantizedTensor<Self>,
84        dim: usize,
85        indices: IntTensor<Self>,
86    ) -> QuantizedTensor<Self> {
87        binary_op!(
88            (tensor, quantized),
89            (indices, int),
90            |tensor, indices| B::q_select(tensor, dim, indices) => Quantized
91        )
92    }
93
94    fn q_slice(tensor: QuantizedTensor<Self>, slices: &[Slice]) -> QuantizedTensor<Self> {
95        unary_op!(tensor, quantized, |tensor| B::q_slice(tensor, slices) => Quantized)
96    }
97
98    fn q_matmul(lhs: TensorPrimitive<Self>, rhs: TensorPrimitive<Self>) -> TensorPrimitive<Self> {
99        // TODO: this would be much cleaner if we consolidated tensor primitive types
100        match (lhs, rhs) {
101            (TensorPrimitive::QFloat(lhs), TensorPrimitive::QFloat(rhs)) => {
102                let propagation = lhs.device().defaults().quantization.propagation;
103                if matches!(propagation, QuantPropagation::Propagate) {
104                    let out = binary_op!(
105                        (lhs, quantized),
106                        (rhs, quantized),
107                        |lhs, rhs| {
108                            if let TensorPrimitive::QFloat(out) = B::q_matmul(
109                                TensorPrimitive::QFloat(lhs),
110                                TensorPrimitive::QFloat(rhs),
111                            ) {
112                                out
113                            } else {
114                                unreachable!()
115                            }
116                        } => Quantized
117                    );
118                    TensorPrimitive::QFloat(out)
119                } else {
120                    let out = binary_op!(
121                        (lhs, quantized),
122                        (rhs, quantized),
123                        |lhs, rhs| {
124                            if let TensorPrimitive::Float(out) = B::q_matmul(
125                                TensorPrimitive::QFloat(lhs),
126                                TensorPrimitive::QFloat(rhs),
127                            ) {
128                                out
129                            } else {
130                                unreachable!()
131                            }
132                        } => Float
133                    );
134                    TensorPrimitive::Float(out)
135                }
136            }
137            (TensorPrimitive::Float(lhs), TensorPrimitive::QFloat(rhs)) => {
138                let propagation = rhs.device().defaults().quantization.propagation;
139                // `binary_float` on the mixed cases: the float side may arrive
140                // autodiff-wrapped, in which case the op runs on the autodiff
141                // backend so gradients flow through the float operand.
142                if matches!(propagation, QuantPropagation::Propagate) {
143                    let out = binary_float!(
144                        (lhs, float),
145                        (rhs, quantized),
146                        |lhs, rhs| {
147                            if let TensorPrimitive::QFloat(out) = B::q_matmul(
148                                TensorPrimitive::Float(lhs),
149                                TensorPrimitive::QFloat(rhs),
150                            ) {
151                                out
152                            } else {
153                                unreachable!()
154                            }
155                        } => Quantized
156                    );
157                    TensorPrimitive::QFloat(out)
158                } else {
159                    let out = binary_float!(
160                        (lhs, float),
161                        (rhs, quantized),
162                        |lhs, rhs| {
163                            if let TensorPrimitive::Float(out) = B::q_matmul(
164                                TensorPrimitive::Float(lhs),
165                                TensorPrimitive::QFloat(rhs),
166                            ) {
167                                out
168                            } else {
169                                unreachable!()
170                            }
171                        } => Float
172                    );
173                    TensorPrimitive::Float(out)
174                }
175            }
176            (TensorPrimitive::QFloat(lhs), TensorPrimitive::Float(rhs)) => {
177                let propagation = lhs.device().defaults().quantization.propagation;
178                if matches!(propagation, QuantPropagation::Propagate) {
179                    let out = binary_float!(
180                        (lhs, quantized),
181                        (rhs, float),
182                        |lhs, rhs| {
183                            if let TensorPrimitive::QFloat(out) = B::q_matmul(
184                                TensorPrimitive::QFloat(lhs),
185                                TensorPrimitive::Float(rhs),
186                            ) {
187                                out
188                            } else {
189                                unreachable!()
190                            }
191                        } => Quantized
192                    );
193                    TensorPrimitive::QFloat(out)
194                } else {
195                    let out = binary_float!(
196                        (lhs, quantized),
197                        (rhs, float),
198                        |lhs, rhs| {
199                            if let TensorPrimitive::Float(out) = B::q_matmul(
200                                TensorPrimitive::QFloat(lhs),
201                                TensorPrimitive::Float(rhs),
202                            ) {
203                                out
204                            } else {
205                                unreachable!()
206                            }
207                        } => Float
208                    );
209                    TensorPrimitive::Float(out)
210                }
211            }
212            _ => unreachable!(),
213        }
214    }
215}