Skip to main content

burn_backend/backend/ops/
qtensor.rs

1use alloc::vec::Vec;
2use burn_std::{
3    BoolDType, FloatDType, IntDType, Shape, Slice,
4    quantization::{QuantPropagation, QuantScheme},
5};
6
7use crate::{
8    Backend, ExecutionError, TensorData, TensorMetadata, TensorPrimitive, get_device_settings,
9};
10use crate::{
11    Scalar,
12    quantization::{Calibration, QuantizationParametersPrimitive, compute_q_params, compute_range},
13    tensor::{BoolTensor, Device, FloatTensor, IntTensor, QuantizedTensor},
14};
15
16/// Automatically applies `dequantization -> float operation -> quantization`.
17///
18/// Used for tensor ops that should always return a quantized output.
19#[macro_export]
20macro_rules! dequant_op_quant {
21    // Binary tensor float op w/ lhs & rhs
22    (
23        float_op $float_op:expr, $t1:expr, $t2:expr
24    ) => {{
25        // Heuristic: prioritize lhs scheme
26        let scheme = $t1.scheme().clone();
27
28        let t1_f = Self::dequantize($t1);
29        let t2_f = Self::dequantize($t2);
30        #[allow(clippy::redundant_closure_call)]
31        let out_f = $float_op(t1_f, t2_f);
32
33        Self::quantize_dynamic(out_f, &scheme)
34    }};
35    // Unary tensor float op
36    (
37        float_op $float_op:expr, $tensor:expr
38    ) => {{
39        let scheme = $tensor.scheme().clone();
40        let dtype = get_device_settings::<B>(&$tensor.device()).float_dtype;
41
42        let tensor_f = Self::dequantize($tensor, dtype);
43        #[allow(clippy::redundant_closure_call)]
44        let out_f = $float_op(tensor_f);
45
46        Self::quantize_dynamic(out_f, &scheme)
47    }};
48}
49
50/// Automatically applies `dequantization -> float operation [-> quantization]`.
51///
52/// The output quantization step is optional.
53/// It is only performed when the input quantization scheme is propagated.
54#[macro_export]
55macro_rules! dequant_op_flow {
56    // Binary tensor float op w/ lhs & rhs
57    (
58        float_op $float_op:expr, $t1:expr, $t2:expr
59    ) => {{
60        // Heuristic: prioritize lhs scheme
61        let scheme = $t1.scheme().clone();
62        let settings = get_device_settings::<B>(&$t1.device());
63        let dtype = settings.float_dtype;
64        let propagation = settings.quantization.propagation;
65
66        let t1_f = Self::dequantize($t1, dtype);
67        let t2_f = Self::dequantize($t2, dtype);
68        #[allow(clippy::redundant_closure_call)]
69        let out_f = $float_op(t1_f, t2_f);
70
71        match propagation {
72            QuantPropagation::Propagate => {
73                TensorPrimitive::QFloat(Self::quantize_dynamic(out_f, &scheme))
74            }
75            QuantPropagation::Inhibit => TensorPrimitive::Float(out_f),
76        }
77    }};
78    // Unary tensor float op
79    (
80        float_op $float_op:expr, $tensor:expr
81    ) => {{
82        let scheme = $tensor.scheme().clone();
83        let settings = get_device_settings::<B>(&$tensor.device());
84        let dtype = settings.float_dtype;
85        let propagation = settings.quantization.propagation;
86
87        let tensor_f = Self::dequantize($tensor, dtype);
88        #[allow(clippy::redundant_closure_call)]
89        let out_f = $float_op(tensor_f);
90
91        match propagation {
92            QuantPropagation::Propagate => {
93                TensorPrimitive::QFloat(Self::quantize_dynamic(out_f, &scheme))
94            }
95            QuantPropagation::Inhibit => TensorPrimitive::Float(out_f),
96        }
97    }};
98}
99
100/// Operations on quantized tensors.
101///
102/// # Return Type Semantics
103///
104/// The return type of each operation indicates how quantization is handled:
105///
106/// ## [`QuantizedTensor<B>`]
107/// If the method returns a `QuantizedTensor<B>`, the operation is expected to preserve the quantized
108/// representation. Implementations should avoid dequantizing when possible to maintain performance.
109/// For example, shape or layout changes such as expand or transpose preserve quantization.
110///
111/// *Note: while this currently doesn't affect the quantized tensor parameters (only per-tensor is
112/// supported at the time of writing), other quantization levels (e.g., per-block) may require re-ordering
113/// the quantization parameters to match the new layout.*
114///
115///
116/// ## [`TensorPrimitive<B>`]
117/// If the method returns a `TensorPrimitive<B>` enum, the return type should align with propagation
118/// strategy specified in the quantization scheme. The output should remain quantized ([`TensorPrimitive::QFloat`])
119/// returned in floating-point form ([`TensorPrimitive::Float`]).
120///
121/// This distinction allows for fine-grained control over mixed-precision flows while still operating
122/// through a unified API.
123pub trait QTensorOps<B: Backend> {
124    /// Creates a new tensor from the data structure.
125    ///
126    /// # Arguments
127    ///
128    /// * `data` - The data structure.
129    /// * `device` - The device to create the tensor on.
130    ///
131    /// # Returns
132    ///
133    /// The tensor with the given data.
134    fn q_from_data(data: TensorData, device: &Device<B>) -> QuantizedTensor<B>;
135
136    /// Convert the tensor to a lower precision data type based on the quantization scheme and parameters.
137    fn quantize(
138        tensor: FloatTensor<B>,
139        scheme: &QuantScheme,
140        qparams: QuantizationParametersPrimitive<B>,
141    ) -> QuantizedTensor<B>;
142
143    /// Dynamically convert the tensor to a lower precision data type based on the quantization scheme.
144    fn quantize_dynamic(tensor: FloatTensor<B>, scheme: &QuantScheme) -> QuantizedTensor<B> {
145        // Dynamically compute min/max tensor range and qparams before quantizing
146        let (min, max) = compute_range::<B>(scheme, tensor.clone(), &Calibration::MinMax);
147        let qparams = compute_q_params(scheme, min, max);
148        Self::quantize(tensor, scheme, qparams)
149    }
150
151    /// Convert the tensor back to a higher precision data type.
152    fn dequantize(tensor: QuantizedTensor<B>, dtype: FloatDType) -> FloatTensor<B>;
153
154    /// Moves the tensor to the given device.
155    ///
156    /// # Arguments
157    ///
158    /// * `tensor` - The tensor.
159    /// * `device` - The device to move the tensor to.
160    ///
161    /// # Returns
162    ///
163    /// The tensor on the given device.
164    fn q_to_device(tensor: QuantizedTensor<B>, device: &Device<B>) -> QuantizedTensor<B>;
165
166    /// Reshapes a tensor.
167    ///
168    /// # Arguments
169    ///
170    /// * `tensor` - The tensor to reshape.
171    /// * `shape` - The new shape of the tensor.
172    ///
173    /// # Returns
174    ///
175    /// The tensor with the new shape.
176    fn q_reshape(tensor: QuantizedTensor<B>, shape: Shape) -> QuantizedTensor<B>;
177
178    /// Converts the tensor to a data structure.
179    ///
180    /// # Arguments
181    ///
182    /// * `tensor` - The tensor.
183    ///
184    /// # Returns
185    ///
186    /// The data structure with the tensor's data.
187    fn q_into_data(
188        tensor: QuantizedTensor<B>,
189    ) -> impl Future<Output = Result<TensorData, ExecutionError>> + Send;
190
191    /// Detaches a tensor from the computation graph.
192    fn q_detach(tensor: QuantizedTensor<B>) -> QuantizedTensor<B> {
193        // Should only be overridden by autodiff backends.
194        tensor
195    }
196
197    /// Sets the `require_grad` flag of a tensor.
198    fn q_set_require_grad(tensor: QuantizedTensor<B>, _require_grad: bool) -> QuantizedTensor<B> {
199        // Should only be overridden by autodiff backends.
200        tensor
201    }
202
203    /// Returns the `require_grad` flag of a tensor.
204    fn q_is_require_grad(_tensor: &QuantizedTensor<B>) -> bool {
205        // Should only be overridden by autodiff backends.
206        false
207    }
208
209    /// Broadcasts the `tensor` to the given `shape`.
210    fn q_expand(tensor: QuantizedTensor<B>, shape: Shape) -> QuantizedTensor<B> {
211        // Default implementation. Backends can expand on the quantized values when supported.
212        dequant_op_quant!(float_op | tensor | B::float_expand(tensor, shape), tensor)
213    }
214
215    /// Transposes a tensor.
216    ///
217    /// # Arguments
218    ///
219    /// * `tensor` - The tensor to transpose.
220    ///
221    /// # Returns
222    ///
223    /// The transposed tensor.
224    fn q_transpose(tensor: QuantizedTensor<B>) -> QuantizedTensor<B> {
225        let ndims = tensor.shape().num_dims();
226        Self::q_swap_dims(tensor, ndims - 2, ndims - 1)
227    }
228
229    /// Swaps two dimensions of a tensor.
230    ///
231    /// # Arguments
232    ///
233    /// * `tensor` - The tensor to swap the dimensions of.
234    /// * `dim1` - The first dimension to swap.
235    /// * `dim2` - The second dimension to swap.
236    ///
237    /// # Returns
238    ///
239    /// The tensor with the dimensions swapped.
240    fn q_swap_dims(tensor: QuantizedTensor<B>, dim1: usize, dim2: usize) -> QuantizedTensor<B>;
241
242    /// Permutes the dimensions of a tensor.
243    ///
244    /// # Arguments
245    ///
246    /// * `tensor` - The tensor to permute the dimensions of.
247    /// * `axes` - The new order of the dimensions.
248    /// # Returns
249    ///
250    /// The tensor with the dimensions permuted.
251    fn q_permute(tensor: QuantizedTensor<B>, axes: &[usize]) -> QuantizedTensor<B>;
252
253    /// Reverse the order of elements in a tensor along the given axes.
254    ///
255    /// # Arguments
256    ///
257    /// * `tensor` - The tensor to reverse.
258    /// * `axes` - The axes to reverse.
259    ///
260    /// The tensor with the elements reversed.
261    fn q_flip(tensor: QuantizedTensor<B>, axes: &[usize]) -> QuantizedTensor<B>;
262
263    /// Select tensor elements along the given dimension corresponding for the given indices.
264    ///
265    /// # Arguments
266    ///
267    /// * `tensor` - The tensor to select from.
268    /// * `dim` - The dimension to select from.
269    /// * `indices` - The indices to select.
270    ///
271    /// # Returns
272    ///
273    /// The selected elements.
274    fn q_select(
275        tensor: QuantizedTensor<B>,
276        dim: usize,
277        indices: IntTensor<B>,
278    ) -> QuantizedTensor<B> {
279        // Default implementation. Backends can select on the quantized values when supported.
280        dequant_op_quant!(
281            float_op | tensor | B::float_select(tensor, dim, indices),
282            tensor
283        )
284    }
285
286    /// Select tensor elements corresponding to the given slices.
287    ///
288    /// # Arguments
289    ///
290    /// * `tensor` - The tensor to select from.
291    /// * `slices` - The slices specifying ranges and steps for each dimension.
292    ///
293    /// # Returns
294    ///
295    /// The selected elements in a new tensor.
296    fn q_slice(tensor: QuantizedTensor<B>, slices: &[Slice]) -> QuantizedTensor<B> {
297        // Default implementation. Backends can slice on the quantized values when supported.
298        dequant_op_quant!(float_op | tensor | B::float_slice(tensor, slices), tensor)
299    }
300
301    /// Gather elements from a tensor.
302    ///
303    /// # Arguments
304    ///
305    /// * `dim` - The dimension to gather from.
306    /// * `tensor` - The tensor to gather from.
307    /// * `indices` - The indices to gather.
308    ///
309    /// # Returns
310    ///
311    /// The gathered elements.
312    fn q_gather(
313        dim: usize,
314        tensor: QuantizedTensor<B>,
315        indices: IntTensor<B>,
316    ) -> QuantizedTensor<B> {
317        // Default implementation. Backends can gather on the quantized values when supported.
318        dequant_op_quant!(
319            float_op | tensor | B::float_gather(dim, tensor, indices),
320            tensor
321        )
322    }
323
324    /// Repeat the tensor along the given dimension.
325    ///
326    /// # Arguments
327    ///
328    /// * `tensor` - The tensor.
329    /// * `dim` - The dimension to repeat.
330    /// * `times` - The number of times to repeat the dimension.
331    ///
332    /// # Returns
333    ///
334    /// The tensor with the given dimension repeated.
335    fn q_repeat_dim(tensor: QuantizedTensor<B>, dim: usize, times: usize) -> QuantizedTensor<B> {
336        dequant_op_quant!(
337            float_op | tensor | B::float_repeat_dim(tensor, dim, times),
338            tensor
339        )
340    }
341
342    /// Adds two tensors together.
343    ///
344    /// # Arguments
345    ///
346    /// * `lhs` - The left hand side tensor.
347    /// * `rhs` - The right hand side tensor.
348    ///
349    /// # Returns
350    ///
351    /// The result of adding the two tensors together.
352    fn q_add(lhs: QuantizedTensor<B>, rhs: QuantizedTensor<B>) -> TensorPrimitive<B> {
353        dequant_op_flow!(float_op | lhs, rhs | B::float_add(lhs, rhs), lhs, rhs)
354    }
355
356    /// Adds a scalar to a tensor.
357    ///
358    /// # Arguments
359    ///
360    /// * `lhs` - The left hand side tensor.
361    /// * `rhs` - The right hand side scalar.
362    ///
363    /// # Returns
364    ///
365    /// The result of adding the scalar to the tensor.
366    fn q_add_scalar(lhs: QuantizedTensor<B>, rhs: Scalar) -> TensorPrimitive<B> {
367        dequant_op_flow!(float_op | tensor | B::float_add_scalar(tensor, rhs), lhs)
368    }
369
370    /// Clamps a tensor under a minimum value.
371    ///
372    /// # Arguments
373    ///
374    /// * `tensor` - The tensor to clamp.
375    /// * `min` - The minimum value.
376    ///
377    /// # Returns
378    ///
379    /// The clamped tensor.
380    fn q_clamp_min(tensor: QuantizedTensor<B>, min: Scalar) -> TensorPrimitive<B> {
381        dequant_op_flow!(float_op | tensor | B::float_clamp_min(tensor, min), tensor)
382    }
383
384    /// Clamps a tensor over a maximum value.
385    ///
386    /// # Arguments
387    ///
388    /// * `tensor` - The tensor to clamp.
389    /// * `max` - The maximum value.
390    ///
391    /// # Returns
392    ///
393    /// The clamped tensor.
394    fn q_clamp_max(tensor: QuantizedTensor<B>, max: Scalar) -> TensorPrimitive<B> {
395        dequant_op_flow!(float_op | tensor | B::float_clamp_max(tensor, max), tensor)
396    }
397
398    /// Clamps a tensor between a minimum and maximum value.
399    ///
400    /// # Arguments
401    ///
402    /// * `tensor` - The tensor to clamp.
403    /// * `min` - The minimum value.
404    /// * `max` - The maximum value.
405    ///
406    /// # Returns
407    ///
408    /// The clamped tensor.
409    fn q_clamp(tensor: QuantizedTensor<B>, min: Scalar, max: Scalar) -> TensorPrimitive<B> {
410        dequant_op_flow!(float_op | tensor | B::float_clamp(tensor, min, max), tensor)
411    }
412
413    /// Subtracts two tensors.
414    ///
415    /// # Arguments
416    ///
417    /// * `lhs` - The left hand side tensor.
418    /// * `rhs` - The right hand side tensor.
419    ///
420    /// # Returns
421    ///
422    /// The result of subtracting the two tensors.
423    fn q_sub(lhs: QuantizedTensor<B>, rhs: QuantizedTensor<B>) -> TensorPrimitive<B> {
424        dequant_op_flow!(float_op | lhs, rhs | B::float_sub(lhs, rhs), lhs, rhs)
425    }
426
427    /// Subtracts a scalar from a tensor.
428    ///
429    /// # Arguments
430    ///
431    /// * `lhs` - The left hand side tensor.
432    /// * `rhs` - The right hand side scalar.
433    ///
434    /// # Returns
435    ///
436    /// The result of subtracting the scalar from the tensor.
437    fn q_sub_scalar(lhs: QuantizedTensor<B>, rhs: Scalar) -> TensorPrimitive<B> {
438        dequant_op_flow!(float_op | tensor | B::float_sub_scalar(tensor, rhs), lhs)
439    }
440
441    /// Multiplies two tensors together element-wise.
442    fn q_mul(lhs: QuantizedTensor<B>, rhs: QuantizedTensor<B>) -> TensorPrimitive<B> {
443        dequant_op_flow!(float_op | lhs, rhs | B::float_mul(lhs, rhs), lhs, rhs)
444    }
445
446    /// Multiplies a tensor by a scalar.
447    ///
448    /// # Arguments
449    ///
450    /// * `lhs` - The left hand side tensor.
451    /// * `rhs` - The right hand side scalar.
452    ///
453    /// # Returns
454    ///
455    /// The result of multiplying the tensor by the scalar.
456    fn q_mul_scalar(lhs: QuantizedTensor<B>, rhs: Scalar) -> TensorPrimitive<B> {
457        dequant_op_flow!(float_op | tensor | B::float_mul_scalar(tensor, rhs), lhs)
458    }
459
460    /// Divides two tensors element-wise.
461    ///
462    /// # Arguments
463    ///
464    /// * `lhs` - The left hand side tensor.
465    /// * `rhs` - The right hand side tensor.
466    ///
467    /// # Returns
468    ///
469    /// The result of dividing the two tensors.
470    fn q_div(lhs: QuantizedTensor<B>, rhs: QuantizedTensor<B>) -> TensorPrimitive<B> {
471        dequant_op_flow!(float_op | lhs, rhs | B::float_div(lhs, rhs), lhs, rhs)
472    }
473
474    /// Divides a tensor by a scalar.
475    ///
476    /// # Arguments
477    ///
478    /// * `lhs` - The left hand side tensor.
479    /// * `rhs` - The right hand side scalar.
480    ///
481    /// # Returns
482    ///
483    /// The result of dividing the tensor by the scalar.
484    fn q_div_scalar(lhs: QuantizedTensor<B>, rhs: Scalar) -> TensorPrimitive<B> {
485        dequant_op_flow!(float_op | tensor | B::float_div_scalar(tensor, rhs), lhs)
486    }
487
488    /// Multiplies two tensors together using matrix multiplication.
489    ///
490    /// # Arguments
491    ///
492    /// * `lhs` - The left hand side tensor.
493    /// * `rhs` - The right hand side tensor.
494    ///
495    /// # Returns
496    ///
497    /// The result of multiplying the two tensors together using matrix multiplication.
498    fn q_matmul(lhs: TensorPrimitive<B>, rhs: TensorPrimitive<B>) -> TensorPrimitive<B> {
499        let mut propagation = QuantPropagation::Inhibit;
500        let mut scheme = QuantScheme::default();
501
502        // Pick a target dtype for any dequantization. If either operand is already
503        // a Float tensor, take its dtype so a Float-QFloat (or QFloat-Float) pair
504        // ends up matching after dequantize and `float_matmul` doesn't see a
505        // dtype mismatch. Only when both operands are QFloat do we fall back to
506        // the device default.
507        let target_dtype: Option<FloatDType> = match (&lhs, &rhs) {
508            (TensorPrimitive::Float(t), _) | (_, TensorPrimitive::Float(t)) => {
509                Some(t.dtype().into())
510            }
511            _ => None,
512        };
513
514        let lhs = match lhs {
515            TensorPrimitive::Float(lhs) => lhs,
516            TensorPrimitive::QFloat(lhs) => {
517                let settings = get_device_settings::<B>(&lhs.device());
518                propagation = settings.quantization.propagation;
519                scheme = lhs.scheme();
520                let float_dtype = target_dtype.unwrap_or(settings.float_dtype);
521
522                Self::dequantize(lhs, float_dtype)
523            }
524        };
525        let rhs = match rhs {
526            TensorPrimitive::Float(rhs) => rhs,
527            TensorPrimitive::QFloat(rhs) => {
528                let settings = get_device_settings::<B>(&rhs.device());
529                propagation = settings.quantization.propagation;
530                scheme = rhs.scheme();
531                let float_dtype = target_dtype.unwrap_or(settings.float_dtype);
532
533                Self::dequantize(rhs, float_dtype)
534            }
535        };
536
537        let out_f = B::float_matmul(lhs, rhs);
538        match propagation {
539            QuantPropagation::Propagate => {
540                TensorPrimitive::QFloat(<Self>::quantize_dynamic(out_f, &scheme))
541            }
542            QuantPropagation::Inhibit => TensorPrimitive::Float(out_f),
543        }
544    }
545
546    /// Negates a tensor element-wise.
547    fn q_neg(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
548        dequant_op_flow!(float_op | tensor | B::float_neg(tensor), tensor)
549    }
550
551    /// Calculates the reciprocals element-wise
552    fn q_recip(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
553        dequant_op_flow!(float_op | tensor | B::float_recip(tensor), tensor)
554    }
555
556    /// Sum of all elements in a tensor.
557    ///
558    /// # Arguments
559    ///
560    /// * `tensor` - The tensor to sum.
561    ///
562    /// # Returns
563    ///
564    /// A scalar tensor with the sum of all elements in `tensor`.
565    fn q_sum(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
566        dequant_op_flow!(float_op | tensor | B::float_sum(tensor), tensor)
567    }
568
569    /// Sum of all elements in a tensor along a dimension.
570    ///
571    /// # Arguments
572    ///
573    /// * `tensor` - The tensor to sum.
574    /// * `dim` - The dimension along which to sum.
575    ///
576    /// # Returns
577    ///
578    /// A tensor with the sum of all elements in `tensor` along `dim`.
579    fn q_sum_dim(tensor: QuantizedTensor<B>, dim: usize) -> TensorPrimitive<B> {
580        dequant_op_flow!(float_op | tensor | B::float_sum_dim(tensor, dim), tensor)
581    }
582
583    /// Product of all elements in a tensor.
584    ///
585    /// # Arguments
586    ///
587    /// * `tensor` - The tensor to product.
588    ///
589    /// # Returns
590    ///
591    /// A scalar tensor with the product of all elements in `tensor`.
592    fn q_prod(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
593        dequant_op_flow!(float_op | tensor | B::float_prod(tensor), tensor)
594    }
595
596    /// Product of all elements in a tensor along a dimension.
597    ///
598    /// # Arguments
599    ///
600    /// * `tensor` - The tensor to product.
601    ///
602    /// # Returns
603    ///
604    /// A tensor with the product of all elements in `tensor` along `dim`.
605    fn q_prod_dim(tensor: QuantizedTensor<B>, dim: usize) -> TensorPrimitive<B> {
606        dequant_op_flow!(float_op | tensor | B::float_prod_dim(tensor, dim), tensor)
607    }
608
609    /// Mean of all elements in a tensor.
610    ///
611    /// # Arguments
612    ///
613    /// * `tensor` - The tensor to mean.
614    ///
615    /// # Returns
616    ///
617    /// A scalar tensor with the mean of all elements in `tensor`.
618    fn q_mean(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
619        dequant_op_flow!(float_op | tensor | B::float_mean(tensor), tensor)
620    }
621
622    /// Mean of all elements in a tensor along a dimension.
623    ///
624    /// # Arguments
625    ///
626    /// * `tensor` - The tensor to mean.
627    /// * `dim` - The dimension along which to mean.
628    ///
629    /// # Returns
630    ///
631    /// A tensor with the mean of all elements in `tensor` along `dim`.
632    fn q_mean_dim(tensor: QuantizedTensor<B>, dim: usize) -> TensorPrimitive<B> {
633        dequant_op_flow!(float_op | tensor | B::float_mean_dim(tensor, dim), tensor)
634    }
635
636    /// Computes the cumulative sum of elements along a dimension.
637    ///
638    /// # Arguments
639    ///
640    /// * `tensor` - The tensor to compute the cumulative sum of.
641    /// * `dim` - The dimension along which to compute the cumulative sum.
642    ///
643    /// # Returns
644    ///
645    /// A tensor with the same shape where each element is the cumulative sum
646    /// of all elements up to and including that position along the dimension.
647    fn q_cumsum(tensor: QuantizedTensor<B>, dim: usize) -> TensorPrimitive<B> {
648        dequant_op_flow!(float_op | tensor | B::float_cumsum(tensor, dim), tensor)
649    }
650
651    /// Computes the cumulative product of elements along a dimension.
652    ///
653    /// # Arguments
654    ///
655    /// * `tensor` - The tensor to compute the cumulative product of.
656    /// * `dim` - The dimension along which to compute the cumulative product.
657    ///
658    /// # Returns
659    ///
660    /// A tensor with the same shape where each element is the cumulative product
661    /// of all elements up to and including that position along the dimension.
662    fn q_cumprod(tensor: QuantizedTensor<B>, dim: usize) -> TensorPrimitive<B> {
663        dequant_op_flow!(float_op | tensor | B::float_cumprod(tensor, dim), tensor)
664    }
665
666    /// Computes the cumulative minimum of elements along a dimension.
667    ///
668    /// # Arguments
669    ///
670    /// * `tensor` - The tensor to compute the cumulative minimum of.
671    /// * `dim` - The dimension along which to compute the cumulative minimum.
672    ///
673    /// # Returns
674    ///
675    /// A tensor with the same shape where each element is the minimum
676    /// of all elements up to and including that position along the dimension.
677    fn q_cummin(tensor: QuantizedTensor<B>, dim: usize) -> TensorPrimitive<B> {
678        dequant_op_flow!(float_op | tensor | B::float_cummin(tensor, dim), tensor)
679    }
680
681    /// Computes the cumulative maximum of elements along a dimension.
682    ///
683    /// # Arguments
684    ///
685    /// * `tensor` - The tensor to compute the cumulative maximum of.
686    /// * `dim` - The dimension along which to compute the cumulative maximum.
687    ///
688    /// # Returns
689    ///
690    /// A tensor with the same shape where each element is the maximum
691    /// of all elements up to and including that position along the dimension.
692    fn q_cummax(tensor: QuantizedTensor<B>, dim: usize) -> TensorPrimitive<B> {
693        dequant_op_flow!(float_op | tensor | B::float_cummax(tensor, dim), tensor)
694    }
695
696    /// Returns a new tensor with exponential values.
697    ///
698    /// # Arguments
699    ///
700    /// * `tensor` - The tensor to exponentiate.
701    ///
702    /// # Returns
703    ///
704    /// A tensor with the same shape as `tensor` with exponential values.
705    fn q_exp(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
706        dequant_op_flow!(float_op | tensor | B::float_exp(tensor), tensor)
707    }
708
709    /// Returns a new tensor with natural logarithm values.
710    ///
711    /// # Arguments
712    ///
713    /// * `tensor` - The tensor to take the logarithm of.
714    ///
715    /// # Returns
716    ///
717    /// A tensor with the same shape as `tensor` with natural logarithm values.
718    fn q_log(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
719        dequant_op_flow!(float_op | tensor | B::float_log(tensor), tensor)
720    }
721
722    /// Returns a new tensor with logarithm values of (1 + Xi).
723    ///
724    /// # Arguments
725    ///
726    /// * `tensor` - The tensor to take the logarithm of.
727    ///
728    /// # Returns
729    ///
730    /// A tensor with the same shape as `tensor` with logarithm values of (1 + Xi).
731    fn q_log1p(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
732        dequant_op_flow!(float_op | tensor | B::float_log1p(tensor), tensor)
733    }
734
735    /// Element-wise power with another tensor.
736    ///
737    /// # Arguments
738    ///
739    /// * `lhs` - The left hand side tensor.
740    /// * `rhs` - The right hand side tensor.
741    ///
742    /// # Returns
743    ///
744    /// The elements of `lhs` raised to the power of the elements of `rhs`.
745    fn q_powf(lhs: QuantizedTensor<B>, rhs: QuantizedTensor<B>) -> TensorPrimitive<B> {
746        dequant_op_flow!(float_op | lhs, rhs | B::float_powf(lhs, rhs), lhs, rhs)
747    }
748
749    /// Element-wise power with an IntTensor.
750    ///
751    /// # Arguments
752    ///
753    /// * `lhs` - The left hand side tensor.
754    /// * `rhs` - The right hand side floatTensor.
755    ///
756    /// # Returns
757    ///
758    /// The elements of `lhs` raised to the value of `rhs`. Result is an IntTensor.
759    fn q_powi(lhs: QuantizedTensor<B>, rhs: IntTensor<B>) -> TensorPrimitive<B> {
760        dequant_op_flow!(float_op | tensor | B::float_powi(tensor, rhs), lhs)
761    }
762
763    /// Element-wise power with an int scalar.
764    ///
765    /// # Arguments
766    ///
767    /// * `lhs` - The left hand side tensor.
768    /// * `rhs` - The right hand side scalar.
769    ///
770    /// # Returns
771    ///
772    /// The elements of `lhs` raised to the value of `rhs`.
773    fn q_powi_scalar(lhs: QuantizedTensor<B>, rhs: Scalar) -> TensorPrimitive<B> {
774        dequant_op_flow!(float_op | tensor | B::float_powi_scalar(tensor, rhs), lhs)
775    }
776
777    /// Element-wise power with a float scalar.
778    ///
779    /// # Arguments
780    ///
781    /// * `tensor` - The tensor to exponentiate.
782    /// * `value` - The exponent.
783    ///
784    /// # Returns
785    ///
786    /// A tensor with the same shape as `tensor` with values raised to the power of `value`.
787    fn q_powf_scalar(tensor: QuantizedTensor<B>, value: Scalar) -> TensorPrimitive<B> {
788        dequant_op_flow!(
789            float_op | tensor | B::float_powf_scalar(tensor, value),
790            tensor
791        )
792    }
793
794    /// Returns a new tensor with square root values.
795    ///
796    /// # Arguments
797    ///
798    /// * `tensor` - The tensor to take the square root of.
799    ///
800    /// # Returns
801    ///
802    /// A tensor with the same shape as `tensor` with square root values.
803    fn q_sqrt(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
804        dequant_op_flow!(float_op | tensor | B::float_sqrt(tensor), tensor)
805    }
806
807    /// Returns a new tensor with absolute values.
808    ///
809    /// # Arguments
810    ///
811    /// * `tensor` - The tensor to take absolute value of.
812    ///
813    /// # Returns
814    ///
815    /// A tensor with the same shape as `tensor` with absolute values.
816    fn q_abs(tensor: QuantizedTensor<B>) -> QuantizedTensor<B> {
817        dequant_op_quant!(float_op | tensor | B::float_abs(tensor), tensor)
818    }
819
820    /// Returns a new tensor with cosine values.
821    ///
822    /// # Arguments
823    ///
824    /// * `tensor` - The tensor to take the cosine of.
825    ///
826    /// # Returns
827    ///
828    /// A tensor with the same shape as `tensor` with cosine values.
829    fn q_cos(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
830        dequant_op_flow!(float_op | tensor | B::float_cos(tensor), tensor)
831    }
832
833    /// Returns a new tensor with sine values.
834    ///
835    /// # Arguments
836    ///
837    /// * `tensor` - The tensor to take the sine of.
838    ///
839    /// # Returns
840    ///
841    /// A tensor with the same shape as `tensor` with sine values.
842    fn q_sin(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
843        dequant_op_flow!(float_op | tensor | B::float_sin(tensor), tensor)
844    }
845
846    /// Returns a new tensor with tangent values.
847    ///
848    /// # Arguments
849    ///
850    /// * `tensor` - The tensor to take the tangent of.
851    ///
852    /// # Returns
853    ///
854    /// A tensor with the same shape as `tensor` with tangent values.
855    fn q_tan(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
856        dequant_op_flow!(float_op | tensor | B::float_tan(tensor), tensor)
857    }
858
859    /// Returns a new tensor with hyperbolic cosine values.
860    ///
861    /// # Arguments
862    ///
863    /// * `tensor` - The tensor to take the hyperbolic cosine of.
864    ///
865    /// # Returns
866    ///
867    /// A tensor with the same shape as `tensor` with hyperbolic cosine values.
868    fn q_cosh(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
869        dequant_op_flow!(float_op | tensor | B::float_cosh(tensor), tensor)
870    }
871
872    /// Returns a new tensor with hyperbolic sine values.
873    ///
874    /// # Arguments
875    ///
876    /// * `tensor` - The tensor to take the hyperbolic sine of.
877    ///
878    /// # Returns
879    ///
880    /// A tensor with the same shape as `tensor` with hyperbolic sine values.
881    fn q_sinh(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
882        dequant_op_flow!(float_op | tensor | B::float_sinh(tensor), tensor)
883    }
884
885    /// Returns a new tensor with hyperbolic tangent values.
886    ///
887    /// # Arguments
888    ///
889    /// * `tensor` - The tensor to take the hyperbolic tangent of.
890    ///
891    /// # Returns
892    ///
893    /// A tensor with the same shape as `tensor` with hyperbolic tangent values.
894    fn q_tanh(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
895        dequant_op_flow!(float_op | tensor | B::float_tanh(tensor), tensor)
896    }
897
898    /// Returns a new tensor with the error function values.
899    ///
900    /// # Arguments
901    ///
902    /// * `tensor` - The tensor to take the error function of.
903    ///
904    /// # Returns
905    ///
906    /// A tensor with the same shape as `tensor` with error function values.
907    fn q_erf(tensor: QuantizedTensor<B>) -> TensorPrimitive<B> {
908        dequant_op_flow!(float_op | tensor | B::float_erf(tensor), tensor)
909    }
910
911    /// Concatenates tensors along a dimension.
912    ///
913    /// # Arguments
914    ///
915    /// * `tensors` - The tensors to concatenate.
916    /// * `dim` - The dimension along which to concatenate.
917    ///
918    /// # Returns
919    ///
920    /// A tensor with the concatenated tensors along `dim`.
921    fn q_cat(tensors: Vec<QuantizedTensor<B>>, dim: usize) -> QuantizedTensor<B> {
922        // Heuristic: prioritize first tensor scheme
923        let first = tensors.first().unwrap();
924        let scheme = first.scheme();
925        let dtype = get_device_settings::<B>(&first.device()).float_dtype;
926
927        let tensor_f = tensors
928            .into_iter()
929            .map(|tensor| Self::dequantize(tensor, dtype))
930            .collect();
931
932        let out_f = B::float_cat(tensor_f, dim);
933
934        Self::quantize_dynamic(out_f, &scheme)
935    }
936
937    /// Gets the indices of the maximum elements of a tensor along an axis.
938    ///
939    /// # Arguments
940    ///
941    /// * `tensor` - The tensor to get the maximum elements of.
942    /// * `dim` - The dimension along which to get the maximum elements.
943    /// * `out_dtype` - The output tensor dtype.
944    ///
945    /// # Returns
946    ///
947    /// A tensor with the indices of the maximum elements of `tensor` along `dim`.
948    fn q_argmax(tensor: QuantizedTensor<B>, dim: usize, out_dtype: IntDType) -> IntTensor<B> {
949        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
950        let tensor_f = Self::dequantize(tensor, dtype);
951        B::float_argmax(tensor_f, dim, out_dtype)
952    }
953
954    /// Gets the indices of the k maximum elements of a tensor along an axis.
955    /// If two elements are equals, order them by the lowest indices
956    ///
957    /// # Arguments
958    ///
959    /// * `tensor` - The tensor to get the k maximum elements of.
960    /// * `dim` - The dimension along which to get the maximum elements.
961    /// * `k` - number of k maximums to keep
962    /// * `out_dtype` - The output tensor dtype.
963    ///
964    /// # Returns
965    ///
966    /// A tensor with the indices of the `k` maximum elements of `tensor` along `dim`.
967    fn q_argtopk(
968        tensor: QuantizedTensor<B>,
969        dim: usize,
970        k: usize,
971        out_dtype: IntDType,
972    ) -> IntTensor<B> {
973        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
974        let tensor_f = Self::dequantize(tensor, dtype);
975        B::float_argtopk(tensor_f, dim, k, out_dtype)
976    }
977
978    /// Gets the values of the k maximum elements of a tensor along an axis.
979    ///
980    /// # Arguments
981    ///
982    /// * `tensor` - The tensor to get the k maximum elements of.
983    /// * `dim` - The dimension along which to get the maximum elements.
984    /// * `k` - number of k maximums to keep
985    /// * `out_dtype` - The output tensor dtype.
986    ///
987    /// # Returns
988    ///
989    /// A tensor with the values of the `k` maximum elements of `tensor` along `dim`.
990    fn q_topk(tensor: QuantizedTensor<B>, dim: usize, k: usize) -> QuantizedTensor<B> {
991        dequant_op_quant!(float_op | tensor | B::float_topk(tensor, dim, k), tensor)
992    }
993
994    /// Gets the values of the k maximum elements of a tensor along an axis, and their indices.
995    ///
996    /// # Arguments
997    ///
998    /// * `tensor` - The tensor to get the k maximum elements of.
999    /// * `dim` - The dimension along which to get the maximum elements.
1000    /// * `k` - number of k maximums to keep
1001    /// * `out_dtype` - The indices tensor dtype.
1002    ///
1003    /// # Returns
1004    ///
1005    /// A tuple with the values of the `k` maximum elements of `tensor` along `dim`, and their
1006    /// indices.
1007    /// The default sorts once and keeps the first `k` of each half, mirroring
1008    /// [`crate::ops::FloatTensorOps::float_topk_with_indices`].
1009    fn q_topk_with_indices(
1010        tensor: QuantizedTensor<B>,
1011        dim: usize,
1012        k: usize,
1013        out_dtype: IntDType,
1014    ) -> (QuantizedTensor<B>, IntTensor<B>) {
1015        let device = tensor.device();
1016        let dtype = get_device_settings::<B>(&device).int_dtype;
1017        let k_indices = B::int_arange(0..k as i64, &device, dtype);
1018        let (values, indices) = Self::q_sort_with_indices(tensor, dim, true, out_dtype);
1019
1020        (
1021            Self::q_select(values, dim, k_indices.clone()),
1022            B::int_select(indices, dim, k_indices),
1023        )
1024    }
1025
1026    /// Gets the indices of the minimum elements of a tensor along an axis.
1027    ///
1028    /// # Arguments
1029    ///
1030    /// * `tensor` - The tensor to get the minimum elements of.
1031    /// * `dim` - The dimension along which to get the minimum elements.
1032    /// * `out_dtype` - The output tensor dtype.
1033    ///
1034    /// # Returns
1035    ///
1036    /// A tensor with the indices of the minimum elements of `tensor` along `dim`.
1037    fn q_argmin(tensor: QuantizedTensor<B>, dim: usize, out_dtype: IntDType) -> IntTensor<B> {
1038        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
1039        let tensor_f = Self::dequantize(tensor, dtype);
1040        B::float_argmin(tensor_f, dim, out_dtype)
1041    }
1042
1043    /// Gets the maximum element of a tensor.
1044    ///
1045    /// # Arguments
1046    ///
1047    /// * `tensor` - The tensor to get the maximum elements of.
1048    ///
1049    /// # Returns
1050    ///
1051    /// A tensor with the maximum element of `tensor`.
1052    fn q_max(tensor: QuantizedTensor<B>) -> QuantizedTensor<B> {
1053        let shape = tensor.shape();
1054        let tensor = B::q_reshape(tensor, Shape::new([shape.num_elements()]));
1055
1056        B::q_max_dim(tensor, 0)
1057    }
1058
1059    /// Gets the maximum elements of a tensor along an axis.
1060    ///
1061    /// # Arguments
1062    ///
1063    /// * `tensor` - The tensor to get the maximum elements of.
1064    /// * `dim` - The dimension along which to get the maximum elements.
1065    ///
1066    /// # Returns
1067    ///
1068    /// A tensor with the maximum elements of `tensor` along `dim`.
1069    fn q_max_dim(tensor: QuantizedTensor<B>, dim: usize) -> QuantizedTensor<B> {
1070        let int_dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1071        let index = B::q_argmax(tensor.clone(), dim, int_dtype);
1072
1073        B::q_gather(dim, tensor, index)
1074    }
1075
1076    /// Gets the maximum elements of a tensor along an axis and their indices.
1077    ///
1078    /// # Arguments
1079    ///
1080    /// * `tensor` - The tensor to get the maximum elements of.
1081    /// * `dim` - The dimension along which to get the maximum elements.
1082    ///
1083    /// # Returns
1084    ///
1085    /// A tuple with the maximum elements of `tensor` along `dim` and their indices.
1086    fn q_max_dim_with_indices(
1087        tensor: QuantizedTensor<B>,
1088        dim: usize,
1089        out_dtype: IntDType,
1090    ) -> (QuantizedTensor<B>, IntTensor<B>) {
1091        let index = B::q_argmax(tensor.clone(), dim, out_dtype);
1092        let values = B::q_gather(dim, tensor, index.clone());
1093
1094        (values, index)
1095    }
1096
1097    /// Gets the minimum element of a tensor.
1098    ///
1099    /// # Arguments
1100    ///
1101    /// * `tensor` - The tensor to get the minimum elements of.
1102    ///
1103    /// # Returns
1104    ///
1105    /// A tensor with the minimum element of `tensor`.
1106    fn q_min(tensor: QuantizedTensor<B>) -> QuantizedTensor<B> {
1107        let shape = tensor.shape();
1108        let tensor = B::q_reshape(tensor, Shape::new([shape.num_elements()]));
1109
1110        B::q_min_dim(tensor, 0)
1111    }
1112
1113    /// Gets the minimum elements of a tensor along an axis.
1114    ///
1115    /// # Arguments
1116    ///
1117    /// * `tensor` - The tensor to get the minimum elements of.
1118    /// * `dim` - The dimension along which to get the minimum elements.
1119    ///
1120    /// # Returns
1121    ///
1122    /// A tensor with the minimum elements of `tensor` along `dim`.
1123    fn q_min_dim(tensor: QuantizedTensor<B>, dim: usize) -> QuantizedTensor<B> {
1124        let int_dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1125        let index = B::q_argmin(tensor.clone(), dim, int_dtype);
1126
1127        B::q_gather(dim, tensor, index)
1128    }
1129
1130    /// Gets the minimum elements of a tensor along an axis and their indices.
1131    ///
1132    /// # Arguments
1133    ///
1134    /// * `tensor` - The tensor to get the minimum elements of.
1135    /// * `dim` - The dimension along which to get the minimum elements.
1136    ///
1137    /// # Returns
1138    ///
1139    /// A tuple with the minimum elements of `tensor` along `dim` and their indices.
1140    fn q_min_dim_with_indices(
1141        tensor: QuantizedTensor<B>,
1142        dim: usize,
1143        out_dtype: IntDType,
1144    ) -> (QuantizedTensor<B>, IntTensor<B>) {
1145        let index = B::q_argmin(tensor.clone(), dim, out_dtype);
1146        let values = B::q_gather(dim, tensor, index.clone());
1147
1148        (values, index)
1149    }
1150
1151    /// Gets the maximum element of a tensor.
1152    ///
1153    /// # Arguments
1154    ///
1155    /// * `tensor` - The tensor to get the maximum elements of.
1156    ///
1157    /// # Returns
1158    ///
1159    /// A tensor with the maximum element of `tensor`.
1160    fn q_max_abs(tensor: QuantizedTensor<B>) -> QuantizedTensor<B> {
1161        let shape = tensor.shape();
1162        let tensor = B::q_reshape(tensor, Shape::new([shape.num_elements()]));
1163
1164        B::q_max_abs_dim(tensor, 0)
1165    }
1166
1167    /// Gets the maximum elements of a tensor along an axis.
1168    ///
1169    /// # Arguments
1170    ///
1171    /// * `tensor` - The tensor to get the maximum elements of.
1172    /// * `dim` - The dimension along which to get the maximum elements.
1173    ///
1174    /// # Returns
1175    ///
1176    /// A tensor with the maximum elements of `tensor` along `dim`.
1177    fn q_max_abs_dim(tensor: QuantizedTensor<B>, dim: usize) -> QuantizedTensor<B> {
1178        let int_dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1179        let index = B::q_argmax(B::q_abs(tensor.clone()), dim, int_dtype);
1180
1181        B::q_gather(dim, tensor, index)
1182    }
1183
1184    /// Tests if any element in the `tensor` evaluates to True.
1185    ///
1186    /// # Arguments
1187    ///
1188    /// * `tensor` - The tensor to test.
1189    ///
1190    /// # Returns
1191    ///
1192    /// A boolean tensor with a single element, True if any element in the tensor is True, False otherwise.
1193    fn q_any(tensor: QuantizedTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1194        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
1195        let tensor_f = Self::dequantize(tensor, dtype);
1196        B::float_any(tensor_f, out_dtype)
1197    }
1198
1199    /// Tests if any element in the float `tensor` evaluates to True along a given dimension `dim`.
1200    ///
1201    /// # Arguments
1202    ///
1203    /// * `tensor` - The tensor to test.
1204    /// * `dim` - The axis along which to test.
1205    ///
1206    /// # Returns
1207    ///
1208    /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1209    /// where the size is 1. The elem in the `dim` axis is True if any element along this dim in the
1210    /// input evaluates to True, False otherwise.
1211    fn q_any_dim(tensor: QuantizedTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1212        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
1213        let tensor_f = Self::dequantize(tensor, dtype);
1214        B::float_any_dim(tensor_f, dim, out_dtype)
1215    }
1216
1217    /// Tests if all elements in the `tensor` evaluate to True.
1218    ///
1219    /// # Arguments
1220    ///
1221    /// * `tensor` - The tensor to test.
1222    ///
1223    /// # Returns
1224    ///
1225    /// A boolean tensor `Tensor<B, 1, Bool>` with a single element, True if all elements in the input tensor
1226    /// evaluate to True, False otherwise.
1227    fn q_all(tensor: QuantizedTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1228        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
1229        let tensor_f = Self::dequantize(tensor, dtype);
1230        B::float_all(tensor_f, out_dtype)
1231    }
1232
1233    /// Tests if all elements in the `tensor` evaluate to True along a given dimension `dim`.
1234    ///
1235    /// # Arguments
1236    ///
1237    /// * `tensor` - The tensor to test.
1238    /// * `dim` - The axis along which to test.
1239    ///
1240    /// # Returns
1241    ///
1242    /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1243    /// where the size is 1. The elem in the `dim` axis is True if all elements along this dim in the input
1244    /// evaluates to True, False otherwise.
1245    fn q_all_dim(tensor: QuantizedTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1246        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
1247        let tensor_f = Self::dequantize(tensor, dtype);
1248        B::float_all_dim(tensor_f, dim, out_dtype)
1249    }
1250
1251    /// Sort the elements of the input `tensor` by value in along a given dimension.
1252    ///
1253    /// This sort is unstable (i.e., may reorder equal elements).
1254    ///
1255    /// # Arguments
1256    ///
1257    /// * `tensor` - The input tensor.
1258    /// * `dim` - The axis along which to sort.
1259    /// * `descending` - The sorting order.
1260    ///
1261    /// # Returns
1262    ///
1263    /// A tensor with the same shape as the input tensor, where the elements are sorted by value.
1264    fn q_sort(tensor: QuantizedTensor<B>, dim: usize, descending: bool) -> QuantizedTensor<B> {
1265        // Default implementation. Backends can sort on the int values since qparams remain the same.
1266        dequant_op_quant!(
1267            float_op | tensor | B::float_sort(tensor, dim, descending),
1268            tensor
1269        )
1270    }
1271
1272    /// Sort the elements of the input `tensor` by value in along a given dimension.
1273    ///
1274    /// This sort is unstable (i.e., may reorder equal elements).
1275    ///
1276    /// # Arguments
1277    ///
1278    /// * `tensor` - The input tensor.
1279    /// * `dim` - The axis along which to sort.
1280    /// * `descending` - The sorting order.
1281    ///
1282    /// # Returns
1283    ///
1284    /// A tensor with the same shape as the input tensor and corresponding indices, where
1285    /// the elements are sorted by value and the indices map back to the original input tensor.
1286    fn q_sort_with_indices(
1287        tensor: QuantizedTensor<B>,
1288        dim: usize,
1289        descending: bool,
1290        out_dtype: IntDType,
1291    ) -> (QuantizedTensor<B>, IntTensor<B>) {
1292        let scheme = tensor.scheme();
1293        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
1294
1295        let tensor_f = Self::dequantize(tensor, dtype);
1296        let (out_f, indices) = B::float_sort_with_indices(tensor_f, dim, descending, out_dtype);
1297
1298        (Self::quantize_dynamic(out_f, &scheme), indices)
1299    }
1300
1301    /// Returns the indices that sort the elements of the input `tensor` by value along a given dimension.
1302    ///
1303    /// This sort is unstable (i.e., may reorder equal elements).
1304    ///
1305    /// # Arguments
1306    ///
1307    /// * `tensor` - The input tensor.
1308    /// * `dim` - The axis along which to sort.
1309    /// * `descending` - The sorting order.
1310    ///
1311    /// # Returns
1312    ///
1313    /// A tensor with the same shape as the input tensor the indices map back to the original input tensor.
1314    fn q_argsort(
1315        tensor: QuantizedTensor<B>,
1316        dim: usize,
1317        descending: bool,
1318        out_dtype: IntDType,
1319    ) -> IntTensor<B> {
1320        let dtype = get_device_settings::<B>(&tensor.device()).float_dtype;
1321        let tensor_f = Self::dequantize(tensor, dtype);
1322        B::float_argsort(tensor_f, dim, descending, out_dtype)
1323    }
1324}