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