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}