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