burn_backend/backend/ops/tensor.rs
1use super::cat::cat_with_slice_assign;
2use super::grid_sample::float_grid_sample_2d_ref;
3use super::repeat_dim::repeat_with_slice_assign;
4use super::sort::{argsort, sort, sort_with_indices};
5use crate::ops::GridSampleOptions;
6use crate::tensor::{BoolTensor, Device, FloatTensor, IntTensor};
7use crate::{Backend, Distribution, TensorData, get_device_settings};
8use crate::{ExecutionError, Scalar, TensorMetadata};
9use alloc::vec::Vec;
10use burn_std::reader::try_read_sync;
11use burn_std::{BoolDType, FloatDType, IndexingUpdateOp, IntDType, Shape, Slice};
12
13/// Operations on float tensors.
14pub trait FloatTensorOps<B: Backend> {
15 /// Creates a new tensor from the data structure.
16 ///
17 /// # Arguments
18 ///
19 /// * `data` - The data structure.
20 /// * `device` - The device to create the tensor on.
21 ///
22 /// # Returns
23 ///
24 /// The tensor with the given data.
25 fn float_from_data(data: TensorData, device: &Device<B>) -> FloatTensor<B>;
26
27 /// Creates a new tensor with random values.
28 ///
29 /// # Arguments
30 ///
31 /// * `shape` - The shape of the tensor.
32 /// * `distribution` - The distribution to sample from.
33 /// * `device` - The device to create the tensor on.
34 /// * `dtype` - The target data type.
35 ///
36 /// # Returns
37 ///
38 /// The tensor with the given shape and random values.
39 fn float_random(
40 shape: Shape,
41 distribution: Distribution,
42 device: &Device<B>,
43 dtype: FloatDType,
44 ) -> FloatTensor<B>;
45
46 /// Creates a new tensor with zeros.
47 ///
48 /// # Arguments
49 ///
50 /// * `shape` - The shape of the tensor.
51 /// * `device` - The device to create the tensor on.
52 /// * `dtype` - The target data type.
53 ///
54 /// # Returns
55 ///
56 /// The tensor with the given shape and zeros.
57 fn float_zeros(shape: Shape, device: &Device<B>, dtype: FloatDType) -> FloatTensor<B> {
58 Self::float_from_data(TensorData::full_dtype(shape, 0., dtype.into()), device)
59 }
60
61 /// Creates a new tensor with ones.
62 ///
63 /// # Arguments
64 ///
65 /// * `shape` - The shape of the tensor.
66 /// * `device` - The device to create the tensor on.
67 /// * `dtype` - The target data type.
68 ///
69 /// # Returns
70 ///
71 /// The tensor with the given shape and ones.
72 fn float_ones(shape: Shape, device: &Device<B>, dtype: FloatDType) -> FloatTensor<B> {
73 Self::float_from_data(TensorData::full_dtype(shape, 1., dtype.into()), device)
74 }
75
76 /// Creates a tensor filled with given value.
77 ///
78 /// # Arguments
79 ///
80 /// * `shape` - The shape of the tensor.
81 /// * `fill_value` - The value with which to fill the tensor.
82 /// * `device` - The device to create the tensor on.
83 /// * `dtype` - The target data type.
84 ///
85 /// # Returns
86 ///
87 /// The tensor filled with given value
88 fn float_full(
89 shape: Shape,
90 fill_value: Scalar,
91 device: &Device<B>,
92 dtype: FloatDType,
93 ) -> FloatTensor<B> {
94 Self::float_from_data(
95 TensorData::full_dtype(shape, fill_value, dtype.into()),
96 device,
97 )
98 }
99
100 /// Converts the tensor to a data structure.
101 ///
102 /// # Arguments
103 ///
104 /// * `tensor` - The tensor.
105 ///
106 /// # Returns
107 ///
108 /// The data structure with the tensor's data.
109 fn float_into_data(
110 tensor: FloatTensor<B>,
111 ) -> impl Future<Output = Result<TensorData, ExecutionError>> + Send;
112
113 /// Moves the tensor to the given device.
114 ///
115 /// # Arguments
116 ///
117 /// * `tensor` - The tensor.
118 /// * `device` - The device to move the tensor to.
119 ///
120 /// # Returns
121 ///
122 /// The tensor on the given device.
123 fn float_to_device(tensor: FloatTensor<B>, device: &Device<B>) -> FloatTensor<B>;
124
125 /// Converts float tensor to int tensor.
126 ///
127 /// # Arguments
128 ///
129 /// * `tensor` - The tensor.
130 /// * `out_dtype` - The output tensor dtype.
131 ///
132 /// # Returns
133 ///
134 /// The int tensor with the same data as the float tensor.
135 fn float_into_int(tensor: FloatTensor<B>, out_dtype: IntDType) -> IntTensor<B>;
136
137 /// Creates an empty tensor with the given shape.
138 ///
139 /// # Arguments
140 ///
141 /// * `shape` - The shape of the tensor.
142 /// * `device` - The device to create the tensor on.
143 /// * `dtype` - The target data type.
144 ///
145 /// # Returns
146 ///
147 /// The empty tensor with the given shape.
148 fn float_empty(shape: Shape, device: &Device<B>, dtype: FloatDType) -> FloatTensor<B>;
149
150 /// Repeat the tensor along the given dimension.
151 ///
152 /// # Arguments
153 ///
154 /// * `tensor` - The tensor.
155 /// * `dim` - The dimension to repeat.
156 /// * `times` - The number of times to repeat the dimension.
157 ///
158 /// # Returns
159 ///
160 /// The tensor with the given dimension repeated.
161 fn float_repeat_dim(tensor: FloatTensor<B>, dim: usize, times: usize) -> FloatTensor<B> {
162 let device = tensor.device();
163 repeat_with_slice_assign::<B, _, _, _>(
164 tensor,
165 dim,
166 times,
167 device,
168 |shape, device, dtype| B::float_empty(shape, device, dtype.into()),
169 B::float_slice_assign,
170 )
171 }
172
173 /// Adds two tensors together.
174 ///
175 /// # Arguments
176 ///
177 /// * `lhs` - The left-hand side tensor.
178 /// * `rhs` - The right-hand side tensor.
179 ///
180 /// # Returns
181 ///
182 /// The result of adding the two tensors together.
183 fn float_add(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
184
185 /// Adds a scalar to a tensor.
186 ///
187 /// # Arguments
188 ///
189 /// * `lhs` - The left-hand side tensor.
190 /// * `rhs` - The right-hand side scalar.
191 ///
192 /// # Returns
193 ///
194 /// The result of adding the scalar to the tensor.
195 fn float_add_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
196
197 /// Clamps a tensor under a minimum value.
198 ///
199 /// # Arguments
200 ///
201 /// * `tensor` - The tensor to clamp.
202 /// * `min` - The minimum value.
203 ///
204 /// # Returns
205 ///
206 /// The clamped tensor.
207 fn float_clamp_min(tensor: FloatTensor<B>, min: Scalar) -> FloatTensor<B> {
208 let dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
209 let mask = Self::float_lower_elem(tensor.clone(), min, dtype);
210 B::float_mask_fill(tensor, mask, min)
211 }
212
213 /// Clamps a tensor over a maximum value.
214 ///
215 /// # Arguments
216 ///
217 /// * `tensor` - The tensor to clamp.
218 /// * `max` - The maximum value.
219 ///
220 /// # Returns
221 ///
222 /// The clamped tensor.
223 fn float_clamp_max(tensor: FloatTensor<B>, max: Scalar) -> FloatTensor<B> {
224 let dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
225 let mask = Self::float_greater_elem(tensor.clone(), max, dtype);
226 B::float_mask_fill(tensor, mask, max)
227 }
228
229 /// Clamps a tensor between a minimum and maximum value.
230 ///
231 /// # Arguments
232 ///
233 /// * `tensor` - The tensor to clamp.
234 /// * `min` - The minimum value.
235 /// * `max` - The maximum value.
236 ///
237 /// # Returns
238 ///
239 /// The clamped tensor.
240 fn float_clamp(tensor: FloatTensor<B>, min: Scalar, max: Scalar) -> FloatTensor<B> {
241 // Default implementation
242 Self::float_clamp_min(Self::float_clamp_max(tensor, max), min)
243 }
244
245 /// Subtracts two tensors.
246 ///
247 /// # Arguments
248 ///
249 /// * `lhs` - The left-hand side tensor.
250 /// * `rhs` - The right-hand side tensor.
251 ///
252 /// # Returns
253 ///
254 /// The result of subtracting the two tensors.
255 fn float_sub(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
256
257 /// Subtracts a scalar from a tensor.
258 ///
259 /// # Arguments
260 ///
261 /// * `lhs` - The left-hand side tensor.
262 /// * `rhs` - The right-hand side scalar.
263 ///
264 /// # Returns
265 ///
266 /// The result of subtracting the scalar from the tensor.
267 fn float_sub_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
268
269 /// Multiplies two tensors together element-wise.
270 fn float_mul(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
271
272 /// Multiplies a tensor by a scalar.
273 ///
274 /// # Arguments
275 ///
276 /// * `lhs` - The left-hand side tensor.
277 /// * `rhs` - The right-hand side scalar.
278 ///
279 /// # Returns
280 ///
281 /// The result of multiplying the tensor by the scalar.
282 fn float_mul_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
283
284 /// Divides two tensors element-wise.
285 ///
286 /// # Arguments
287 ///
288 /// * `lhs` - The left-hand side tensor.
289 /// * `rhs` - The right-hand side tensor.
290 ///
291 /// # Returns
292 ///
293 /// The result of dividing the two tensors.
294 fn float_div(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
295
296 /// Divides a tensor by a scalar.
297 ///
298 /// # Arguments
299 ///
300 /// * `lhs` - The left-hand side tensor.
301 /// * `rhs` - The right-hand side scalar.
302 ///
303 /// # Returns
304 ///
305 /// The result of dividing the tensor by the scalar.
306 fn float_div_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
307
308 /// Computes the remainder of division between two tensors element-wise.
309 ///
310 /// # Arguments
311 ///
312 /// * `lhs` - The left-hand side tensor.
313 /// * `rhs` - The right-hand side tensor.
314 ///
315 /// # Returns
316 ///
317 /// The element-wise remainder when dividing `lhs` by `rhs`.
318 fn float_remainder(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
319
320 /// Computes the modulus of a tensor given a scalar.
321 ///
322 /// # Arguments
323 /// * `lhs` - The left-hand side tensor.
324 /// * `rhs` - The right-hand side scalar.
325 ///
326 /// # Returns
327 ///
328 /// The result of applying the modulus of the scalar to the tensor.
329 fn float_remainder_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
330
331 /// Multiplies two tensors together using matrix multiplication.
332 ///
333 /// # Arguments
334 ///
335 /// * `lhs` - The left-hand side tensor.
336 /// * `rhs` - The right-hand side tensor.
337 ///
338 /// # Returns
339 ///
340 /// The result of multiplying the two tensors together using matrix multiplication.
341 fn float_matmul(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
342
343 /// Computes the cross product of two tensors along a given dimension.
344 ///
345 /// # Arguments
346 ///
347 /// * `lhs` - The left-hand side tensor.
348 /// * `rhs` - The right-hand side tensor.
349 /// * `dim` - The dimension to compute the cross product along.
350 ///
351 /// # Returns
352 ///
353 /// The cross product of the two tensors.
354 fn float_cross(lhs: FloatTensor<B>, rhs: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
355
356 /// Negates a tensor element-wise.
357 fn float_neg(tensor: FloatTensor<B>) -> FloatTensor<B> {
358 Self::float_mul_scalar(tensor, (-1f32).into())
359 }
360
361 /// Calculates the reciprocals element-wise
362 fn float_recip(tensor: FloatTensor<B>) -> FloatTensor<B>;
363
364 /// Transposes a tensor.
365 ///
366 /// # Arguments
367 ///
368 /// * `tensor` - The tensor to transpose.
369 ///
370 /// # Returns
371 ///
372 /// The transposed tensor.
373 fn float_transpose(tensor: FloatTensor<B>) -> FloatTensor<B> {
374 let ndims = tensor.shape().num_dims();
375 Self::float_swap_dims(tensor, ndims - 2, ndims - 1)
376 }
377
378 /// Swaps two dimensions of a tensor.
379 ///
380 /// # Arguments
381 ///
382 /// * `tensor` - The tensor to swap the dimensions of.
383 /// * `dim1` - The first dimension to swap.
384 /// * `dim2` - The second dimension to swap.
385 ///
386 /// # Returns
387 ///
388 /// The tensor with the dimensions swapped.
389 fn float_swap_dims(tensor: FloatTensor<B>, dim1: usize, dim2: usize) -> FloatTensor<B>;
390
391 /// Permutes the dimensions of a tensor.
392 ///
393 /// # Arguments
394 ///
395 /// * `tensor` - The tensor to permute the dimensions of.
396 /// * `axes` - The new order of the dimensions.
397 /// # Returns
398 ///
399 /// The tensor with the dimensions permuted.
400 fn float_permute(tensor: FloatTensor<B>, axes: &[usize]) -> FloatTensor<B>;
401
402 /// Reverse the order of elements in a tensor along the given axes.
403 ///
404 /// # Arguments
405 ///
406 /// * `tensor` - The tensor to reverse.
407 /// * `axes` - The axes to reverse.
408 ///
409 /// The tensor with the elements reversed.
410 fn float_flip(tensor: FloatTensor<B>, axes: &[usize]) -> FloatTensor<B>;
411
412 /// Reshapes a tensor.
413 ///
414 /// # Arguments
415 ///
416 /// * `tensor` - The tensor to reshape.
417 /// * `shape` - The new shape of the tensor.
418 ///
419 /// # Returns
420 ///
421 /// The tensor with the new shape.
422 fn float_reshape(tensor: FloatTensor<B>, shape: Shape) -> FloatTensor<B>;
423
424 /// Gather elements from a tensor.
425 ///
426 /// # Arguments
427 ///
428 /// * `dim` - The dimension to gather from.
429 /// * `tensor` - The tensor to gather from.
430 /// * `indices` - The indices to gather.
431 ///
432 /// # Returns
433 ///
434 /// The gathered elements.
435 fn float_gather(dim: usize, tensor: FloatTensor<B>, indices: IntTensor<B>) -> FloatTensor<B>;
436
437 /// Scatter elements into a tensor using sum reduction.
438 ///
439 /// # Arguments
440 ///
441 /// * `dim` - The dimension to scatter into.
442 /// * `tensor` - The tensor to scatter into.
443 /// * `indices` - The indices to scatter into.
444 /// * `value` - The value to scatter.
445 ///
446 /// # Returns
447 ///
448 /// The tensor with the scattered elements.
449 fn float_scatter_add(
450 dim: usize,
451 tensor: FloatTensor<B>,
452 indices: IntTensor<B>,
453 value: FloatTensor<B>,
454 ) -> FloatTensor<B>;
455
456 /// Scatter elements into a tensor using the specified update operation.
457 ///
458 /// Backend implementations may override this to support operations beyond add.
459 fn float_scatter(
460 dim: usize,
461 tensor: FloatTensor<B>,
462 indices: IntTensor<B>,
463 value: FloatTensor<B>,
464 update: IndexingUpdateOp,
465 ) -> FloatTensor<B> {
466 match update {
467 IndexingUpdateOp::Add => Self::float_scatter_add(dim, tensor, indices, value),
468 other => unimplemented!("float_scatter with {other:?} update is not implemented"),
469 }
470 }
471
472 /// Multi-dimensional scatter: update `data` at locations specified by `indices` with `values`.
473 ///
474 /// # Arguments
475 ///
476 /// * `data` - The tensor to scatter into.
477 /// * `indices` - An M-dimensional integer tensor whose last dimension indexes into `data`.
478 /// * `values` - The values to scatter.
479 /// * `reduction` - How to combine with existing values.
480 ///
481 /// # Returns
482 ///
483 /// The tensor with scattered values.
484 fn float_scatter_nd(
485 _data: FloatTensor<B>,
486 _indices: IntTensor<B>,
487 _values: FloatTensor<B>,
488 _reduction: crate::tensor::IndexingUpdateOp,
489 ) -> FloatTensor<B> {
490 unimplemented!("float_scatter_nd is not implemented for this backend")
491 }
492
493 /// Multi-dimensional gather: collect slices from `data` at locations specified by `indices`.
494 ///
495 /// # Arguments
496 ///
497 /// * `data` - The tensor to gather from.
498 /// * `indices` - An M-dimensional integer tensor whose last dimension indexes into `data`.
499 ///
500 /// # Returns
501 ///
502 /// The gathered tensor.
503 fn float_gather_nd(_data: FloatTensor<B>, _indices: IntTensor<B>) -> FloatTensor<B> {
504 unimplemented!("float_gather_nd is not implemented for this backend")
505 }
506
507 /// Select tensor elements along the given dimension corresponding for the given indices.
508 ///
509 /// # Arguments
510 ///
511 /// * `tensor` - The tensor to select from.
512 /// * `dim` - The dimension to select from.
513 /// * `indices` - The indices to select.
514 ///
515 /// # Returns
516 ///
517 /// The selected elements.
518 fn float_select(tensor: FloatTensor<B>, dim: usize, indices: IntTensor<B>) -> FloatTensor<B>;
519
520 /// Assign the selected elements along the given dimension corresponding for the given indices
521 /// to the given value using sum reduction.
522 ///
523 /// # Arguments
524 ///
525 /// * `tensor` - The tensor to select from.
526 /// * `dim` - The dimension to select from.
527 /// * `indices` - The indices to select.
528 /// * `value` - The value to assign.
529 ///
530 /// # Returns
531 ///
532 /// The tensor with the selected elements assigned to the given value.
533 fn float_select_add(
534 tensor: FloatTensor<B>,
535 dim: usize,
536 indices: IntTensor<B>,
537 value: FloatTensor<B>,
538 ) -> FloatTensor<B>;
539
540 /// Assign selected elements along a dimension using the specified update operation.
541 ///
542 /// Backend implementations may override this to support operations beyond add.
543 fn float_select_assign(
544 tensor: FloatTensor<B>,
545 dim: usize,
546 indices: IntTensor<B>,
547 value: FloatTensor<B>,
548 update: IndexingUpdateOp,
549 ) -> FloatTensor<B> {
550 match update {
551 IndexingUpdateOp::Add => Self::float_select_add(tensor, dim, indices, value),
552 other => {
553 unimplemented!("float_select_assign with {other:?} update is not implemented")
554 }
555 }
556 }
557
558 /// Select tensor elements corresponding to the given slices.
559 ///
560 /// # Arguments
561 ///
562 /// * `tensor` - The tensor to select from.
563 /// * `slices` - The slices specifying ranges and steps for each dimension.
564 ///
565 /// # Returns
566 ///
567 /// The selected elements in a new tensor.
568 ///
569 /// # Note
570 ///
571 /// Empty slices (where start >= end) are handled at the high-level tensor API and will not
572 /// be passed to this method. Backend implementations do not need to handle empty slices.
573 fn float_slice(tensor: FloatTensor<B>, slices: &[Slice]) -> FloatTensor<B>;
574
575 /// Assign the selected elements corresponding to the given slices to the given value.
576 ///
577 /// # Arguments
578 ///
579 /// * `tensor` - The tensor to select from.
580 /// * `ranges` - The ranges to select.
581 /// * `value` - The value to assign.
582 ///
583 /// # Returns
584 ///
585 /// The tensor with the selected elements assigned to the given value.
586 ///
587 /// # Note
588 ///
589 /// Empty slice assignments (where any slice range produces 0 elements) are handled at the
590 /// high-level tensor API and will not be passed to this method. Backend implementations do
591 /// not need to handle empty slice assignments.
592 fn float_slice_assign(
593 tensor: FloatTensor<B>,
594 slices: &[Slice],
595 value: FloatTensor<B>,
596 ) -> FloatTensor<B>;
597
598 /// Update the given tensor with the value tensor where the mask is true.
599 ///
600 /// # Arguments
601 ///
602 /// * `tensor` - The tensor to select from.
603 /// * `mask` - The boolean mask to select with.
604 /// * `value` - The value to assign to the selected elements from the value tensor.
605 ///
606 /// # Returns
607 ///
608 /// The tensor with the selected elements assigned to the given value.
609 fn float_mask_where(
610 tensor: FloatTensor<B>,
611 mask: BoolTensor<B>,
612 value: FloatTensor<B>,
613 ) -> FloatTensor<B>;
614
615 /// Update the given tensor with the value where the mask is true.
616 ///
617 /// # Arguments
618 ///
619 /// * `tensor` - The tensor to select from.
620 /// * `mask` - The boolean mask to select with.
621 /// * `value` - The value to assign to the selected elements.
622 ///
623 /// # Returns
624 ///
625 /// The tensor with the selected elements assigned to the given value.
626 fn float_mask_fill(
627 tensor: FloatTensor<B>,
628 mask: BoolTensor<B>,
629 value: Scalar,
630 ) -> FloatTensor<B>;
631
632 /// Selects the elements of the tensor where the mask is true, returned as a 1D tensor.
633 ///
634 /// The elements are collected in row-major order. Because the number of selected elements
635 /// depends on the mask values, the output shape is data-dependent: computing it may require
636 /// synchronizing with the device, which is why this operation is asynchronous.
637 ///
638 /// # Arguments
639 ///
640 /// * `tensor` - The tensor to select from.
641 /// * `mask` - The boolean mask, with the same shape as the tensor.
642 ///
643 /// # Returns
644 ///
645 /// A 1D tensor containing the selected elements.
646 fn float_mask_select(
647 tensor: FloatTensor<B>,
648 mask: BoolTensor<B>,
649 ) -> impl Future<Output = FloatTensor<B>> + 'static + Send {
650 async move {
651 // Data-dependent output length, so we defer to `bool_argwhere` (the only pre-existing
652 // data-dependent op) to collect the flat indices of the true mask values, then select.
653 let n = mask.shape().num_elements();
654 let int_dtype = get_device_settings::<B>(&mask.device()).int_dtype;
655 let mask = B::bool_reshape(mask, Shape::new([n]));
656 let indices = B::bool_argwhere(mask, int_dtype).await; // [count, 1]
657 let count = indices.shape()[0];
658 let indices = B::int_reshape(indices, Shape::new([count])); // squeeze to [count]
659 let tensor = B::float_reshape(tensor, Shape::new([n]));
660 B::float_select(tensor, 0, indices)
661 }
662 }
663
664 /// Equal comparison of two tensors.
665 ///
666 /// # Arguments
667 ///
668 /// * `lhs` - The left-hand side tensor.
669 /// * `rhs` - The right-hand side tensor.
670 /// * `out_dtype` - The output tensor dtype.
671 ///
672 /// # Returns
673 ///
674 /// A boolean tensor with the result of the comparison.
675 fn float_equal(lhs: FloatTensor<B>, rhs: FloatTensor<B>, out_dtype: BoolDType)
676 -> BoolTensor<B>;
677
678 /// Element-wise non-equality comparison.
679 ///
680 /// # Arguments
681 ///
682 /// * `lhs` - The left-hand side tensor.
683 /// * `rhs` - The right-hand side tensor.
684 /// * `out_dtype` - The output tensor dtype.
685 ///
686 /// # Returns
687 ///
688 /// A boolean tensor with the result of the comparison.
689 fn float_not_equal(
690 lhs: FloatTensor<B>,
691 rhs: FloatTensor<B>,
692 out_dtype: BoolDType,
693 ) -> BoolTensor<B> {
694 let equal_tensor = B::float_equal(lhs, rhs, out_dtype);
695 B::bool_not(equal_tensor)
696 }
697
698 /// Equal comparison of a tensor and a scalar.
699 ///
700 /// # Arguments
701 ///
702 /// * `lhs` - The left-hand side tensor.
703 /// * `rhs` - The right-hand side scalar.
704 /// * `out_dtype` - The output tensor dtype.
705 ///
706 /// # Returns
707 ///
708 /// A boolean tensor with the result of the comparison.
709 fn float_equal_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
710
711 /// Element-wise non-equality comparison with a scalar.
712 ///
713 /// # Arguments
714 ///
715 /// * `lhs` - The left-hand side tensor.
716 /// * `rhs` - The right-hand side scalar.
717 /// * `out_dtype` - The output tensor dtype.
718 ///
719 /// # Returns
720 ///
721 /// A boolean tensor with the result of the comparison.
722 fn float_not_equal_elem(
723 lhs: FloatTensor<B>,
724 rhs: Scalar,
725 out_dtype: BoolDType,
726 ) -> BoolTensor<B> {
727 let equal_tensor = B::float_equal_elem(lhs, rhs, out_dtype);
728 B::bool_not(equal_tensor)
729 }
730
731 /// Greater than comparison of two tensors.
732 ///
733 /// # Arguments
734 ///
735 /// * `lhs` - The left-hand side tensor.
736 /// * `rhs` - The right-hand side tensor.
737 /// * `out_dtype` - The output tensor dtype.
738 ///
739 /// # Returns
740 ///
741 /// A boolean tensor with the result of the comparison.
742 fn float_greater(
743 lhs: FloatTensor<B>,
744 rhs: FloatTensor<B>,
745 out_dtype: BoolDType,
746 ) -> BoolTensor<B>;
747
748 /// Greater than comparison of a tensor and a scalar.
749 ///
750 /// # Arguments
751 ///
752 /// * `lhs` - The left-hand side tensor.
753 /// * `rhs` - The right-hand side scalar.
754 /// * `out_dtype` - The output tensor dtype.
755 ///
756 /// # Returns
757 ///
758 /// A boolean tensor with the result of the comparison.
759 fn float_greater_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
760
761 /// Greater than or equal comparison of two tensors.
762 ///
763 /// # Arguments
764 ///
765 /// * `lhs` - The left-hand side tensor.
766 /// * `rhs` - The right-hand side tensor.
767 /// * `out_dtype` - The output tensor dtype.
768 ///
769 /// # Returns
770 ///
771 /// A boolean tensor with the result of the comparison.
772 fn float_greater_equal(
773 lhs: FloatTensor<B>,
774 rhs: FloatTensor<B>,
775 out_dtype: BoolDType,
776 ) -> BoolTensor<B>;
777
778 /// Greater than or equal comparison of a tensor and a scalar.
779 ///
780 /// # Arguments
781 ///
782 /// * `lhs` - The left-hand side tensor.
783 /// * `rhs` - The right-hand side scalar.
784 /// * `out_dtype` - The output tensor dtype.
785 ///
786 /// # Returns
787 ///
788 /// A boolean tensor with the result of the comparison.
789 fn float_greater_equal_elem(
790 lhs: FloatTensor<B>,
791 rhs: Scalar,
792 out_dtype: BoolDType,
793 ) -> BoolTensor<B>;
794
795 /// Less than comparison of two tensors.
796 ///
797 /// # Arguments
798 ///
799 /// * `lhs` - The left-hand side tensor.
800 /// * `rhs` - The right-hand side tensor.
801 /// * `out_dtype` - The output tensor dtype.
802 ///
803 /// # Returns
804 ///
805 /// A boolean tensor with the result of the comparison.
806 fn float_lower(lhs: FloatTensor<B>, rhs: FloatTensor<B>, out_dtype: BoolDType)
807 -> BoolTensor<B>;
808
809 /// Less than comparison of a tensor and a scalar.
810 ///
811 /// # Arguments
812 ///
813 /// * `lhs` - The left-hand side tensor.
814 /// * `rhs` - The right-hand side scalar.
815 /// * `out_dtype` - The output tensor dtype.
816 ///
817 /// # Returns
818 ///
819 /// A boolean tensor with the result of the comparison.
820 fn float_lower_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
821
822 /// Less than or equal comparison of two tensors.
823 ///
824 /// # Arguments
825 ///
826 /// * `lhs` - The left-hand side tensor.
827 /// * `rhs` - The right-hand side tensor.
828 /// * `out_dtype` - The output tensor dtype.
829 ///
830 /// # Returns
831 ///
832 /// A boolean tensor with the result of the comparison.
833 fn float_lower_equal(
834 lhs: FloatTensor<B>,
835 rhs: FloatTensor<B>,
836 out_dtype: BoolDType,
837 ) -> BoolTensor<B>;
838
839 /// Less than or equal comparison of a tensor and a scalar.
840 ///
841 /// # Arguments
842 ///
843 /// * `lhs` - The left-hand side tensor.
844 /// * `rhs` - The right-hand side scalar.
845 /// * `out_dtype` - The output tensor dtype.
846 ///
847 /// # Returns
848 ///
849 /// A boolean tensor with the result of the comparison.
850 fn float_lower_equal_elem(
851 lhs: FloatTensor<B>,
852 rhs: Scalar,
853 out_dtype: BoolDType,
854 ) -> BoolTensor<B>;
855
856 /// Detaches a tensor from the computation graph.
857 fn float_detach(tensor: FloatTensor<B>) -> FloatTensor<B> {
858 // Should only be overridden by autodiff backends.
859 tensor
860 }
861
862 /// Sets the `require_grad` flag of a tensor.
863 fn float_set_require_grad(tensor: FloatTensor<B>, _require_grad: bool) -> FloatTensor<B> {
864 // Should only be overridden by autodiff backends.
865 tensor
866 }
867
868 /// Returns the `require_grad` flag of a tensor.
869 fn float_is_require_grad(_tensor: &FloatTensor<B>) -> bool {
870 // Should only be overridden by autodiff backends.
871 false
872 }
873
874 /// Sum of all elements in a tensor.
875 ///
876 /// # Arguments
877 ///
878 /// * `tensor` - The tensor to sum.
879 ///
880 /// # Returns
881 ///
882 /// A scalar tensor with the sum of all elements in `tensor`.
883 fn float_sum(tensor: FloatTensor<B>) -> FloatTensor<B>;
884
885 /// Sum of all elements in a tensor along a dimension.
886 ///
887 /// # Arguments
888 ///
889 /// * `tensor` - The tensor to sum.
890 /// * `dim` - The dimension along which to sum.
891 ///
892 /// # Returns
893 ///
894 /// A tensor with the sum of all elements in `tensor` along `dim`.
895 fn float_sum_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
896
897 /// Product of all elements in a tensor.
898 ///
899 /// # Arguments
900 ///
901 /// * `tensor` - The tensor to product.
902 ///
903 /// # Returns
904 ///
905 /// A scalar tensor with the product of all elements in `tensor`.
906 fn float_prod(tensor: FloatTensor<B>) -> FloatTensor<B> {
907 // Product of all elements in a tensor
908 B::float_exp(B::float_sum(B::float_log(tensor)))
909 }
910
911 /// Product of all elements in a tensor along a dimension.
912 ///
913 /// # Arguments
914 ///
915 /// * `tensor` - The tensor to product.
916 ///
917 /// # Returns
918 ///
919 /// A tensor with the product of all elements in `tensor` along `dim`.
920 fn float_prod_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
921 // Product of all elements in a tensor along a dimension
922 B::float_exp(B::float_sum_dim(B::float_log(tensor), dim))
923 }
924
925 /// Mean of all elements in a tensor.
926 ///
927 /// # Arguments
928 ///
929 /// * `tensor` - The tensor to mean.
930 ///
931 /// # Returns
932 ///
933 /// A scalar tensor with the mean of all elements in `tensor`.
934 fn float_mean(tensor: FloatTensor<B>) -> FloatTensor<B> {
935 let num_elems = tensor.shape().num_elements() as f32;
936 B::float_div_scalar(B::float_sum(tensor), num_elems.into())
937 }
938
939 /// Mean of all elements in a tensor along a dimension.
940 ///
941 /// # Arguments
942 ///
943 /// * `tensor` - The tensor to mean.
944 /// * `dim` - The dimension along which to mean.
945 ///
946 /// # Returns
947 ///
948 /// A tensor with the mean of all elements in `tensor` along `dim`.
949 fn float_mean_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
950
951 /// Computes the cumulative sum of elements along a dimension.
952 ///
953 /// # Arguments
954 ///
955 /// * `tensor` - The tensor to compute the cumulative sum of.
956 /// * `dim` - The dimension along which to compute the cumulative sum.
957 ///
958 /// # Returns
959 ///
960 /// A tensor with the same shape where each element is the cumulative sum
961 /// of all elements up to and including that position along the dimension.
962 fn float_cumsum(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
963
964 /// Computes the cumulative product of elements along a dimension.
965 ///
966 /// # Arguments
967 ///
968 /// * `tensor` - The tensor to compute the cumulative product of.
969 /// * `dim` - The dimension along which to compute the cumulative product.
970 ///
971 /// # Returns
972 ///
973 /// A tensor with the same shape where each element is the cumulative product
974 /// of all elements up to and including that position along the dimension.
975 fn float_cumprod(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
976
977 /// Computes the cumulative minimum of elements along a dimension.
978 ///
979 /// # Arguments
980 ///
981 /// * `tensor` - The tensor to compute the cumulative minimum of.
982 /// * `dim` - The dimension along which to compute the cumulative minimum.
983 ///
984 /// # Returns
985 ///
986 /// A tensor with the same shape where each element is the minimum
987 /// of all elements up to and including that position along the dimension.
988 fn float_cummin(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
989
990 /// Computes the cumulative maximum of elements along a dimension.
991 ///
992 /// # Arguments
993 ///
994 /// * `tensor` - The tensor to compute the cumulative maximum of.
995 /// * `dim` - The dimension along which to compute the cumulative maximum.
996 ///
997 /// # Returns
998 ///
999 /// A tensor with the same shape where each element is the maximum
1000 /// of all elements up to and including that position along the dimension.
1001 fn float_cummax(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
1002
1003 /// Converts a tensor to another floating point data type.
1004 ///
1005 /// # Arguments
1006 ///
1007 /// * `tensor` - The tensor to convert.
1008 /// * `dtype` - The target data type.
1009 ///
1010 /// # Returns
1011 ///
1012 /// A tensor with the same values as `tensor` but in the target floating point data type.
1013 fn float_cast(tensor: FloatTensor<B>, dtype: FloatDType) -> FloatTensor<B>;
1014
1015 /// Returns a new tensor with exponential values.
1016 ///
1017 /// # Arguments
1018 ///
1019 /// * `tensor` - The tensor to exponentiate.
1020 ///
1021 /// # Returns
1022 ///
1023 /// A tensor with the same shape as `tensor` with exponential values.
1024 fn float_exp(tensor: FloatTensor<B>) -> FloatTensor<B>;
1025
1026 /// Returns a new tensor with natural logarithm values.
1027 ///
1028 /// # Arguments
1029 ///
1030 /// * `tensor` - The tensor to take the logarithm of.
1031 ///
1032 /// # Returns
1033 ///
1034 /// A tensor with the same shape as `tensor` with natural logarithm values.
1035 fn float_log(tensor: FloatTensor<B>) -> FloatTensor<B>;
1036
1037 /// Returns a new tensor with logarithm values of (1 + Xi).
1038 ///
1039 /// # Arguments
1040 ///
1041 /// * `tensor` - The tensor to take the logarithm of.
1042 ///
1043 /// # Returns
1044 ///
1045 /// A tensor with the same shape as `tensor` with logarithm values of (1 + Xi).
1046 fn float_log1p(tensor: FloatTensor<B>) -> FloatTensor<B>;
1047
1048 /// Element-wise power with a FloatTensor.
1049 ///
1050 /// # Arguments
1051 ///
1052 /// * `lhs` - The left-hand side tensor.
1053 /// * `rhs` - The right-hand side tensor.
1054 ///
1055 /// # Returns
1056 ///
1057 /// The elements of `lhs` raised to the power of the elements of `rhs`.
1058 fn float_powf(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
1059
1060 /// Element-wise power with an IntTensor.
1061 ///
1062 /// # Arguments
1063 ///
1064 /// * `lhs` - The left-hand side tensor.
1065 /// * `rhs` - The right-hand side floatTensor.
1066 ///
1067 /// # Returns
1068 ///
1069 /// The elements of `lhs` raised to the value of `rhs`. Result is an IntTensor.
1070 fn float_powi(lhs: FloatTensor<B>, rhs: IntTensor<B>) -> FloatTensor<B> {
1071 let dtype = lhs.dtype();
1072 Self::float_powf(lhs, B::int_into_float(rhs, dtype.into()))
1073 }
1074
1075 /// Raises a tensor to the power of an int scalar.
1076 ///
1077 /// # Backend Implementors Note
1078 ///
1079 /// A number of common exponent cases can be implemented with operations
1080 /// which are much cheaper than generic exponentiation.
1081 ///
1082 /// This (`Backend` impl overridable) operation handles generic optimizations
1083 /// for several common integer exponent cases; and then dispatches to
1084 /// the (`Backend` impl overridable) [`Self::float_powi_scalar_impl`]
1085 /// operation to handle the generic case.
1086 ///
1087 /// # Arguments
1088 ///
1089 /// * `lhs` - The left-hand side tensor.
1090 /// * `rhs` - The right-hand side scalar.
1091 ///
1092 /// # Returns
1093 ///
1094 /// The elements of `lhs` raised to the value of `rhs`.
1095 fn float_powi_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B> {
1096 match rhs.elem::<i64>() {
1097 0 => Self::float_ones(lhs.shape(), &lhs.device(), lhs.dtype().into()),
1098 1 => lhs,
1099 2 => B::float_mul(lhs.clone(), lhs),
1100 -1 => Self::float_recip(lhs),
1101 -2 => Self::float_recip(B::float_mul(lhs.clone(), lhs)),
1102 _ => Self::float_powi_scalar_impl(lhs, rhs),
1103 }
1104 }
1105
1106 /// Raises a tensor to the power of an int scalar.
1107 ///
1108 /// # Backend Implementors Note
1109 ///
1110 /// This is the generic implementation of integer exponentiation
1111 /// called by [`Self::float_powi_scalar`] in the fallback case.
1112 ///
1113 /// As a general rule, this should not be called directly.
1114 ///
1115 /// # Arguments
1116 ///
1117 /// * `lhs` - The left-hand side tensor.
1118 /// * `rhs` - The right-hand side scalar.
1119 ///
1120 /// # Returns
1121 ///
1122 /// The elements of `lhs` raised to the value of `rhs`.
1123 fn float_powi_scalar_impl(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B> {
1124 // Avoid a recursive loop by deferring directly to float_powf_scalar_impl.
1125 Self::float_powf_scalar_impl(lhs, rhs)
1126 }
1127
1128 /// Returns a new tensor with values raised to the power of float `value`.
1129 ///
1130 /// # Backend Implementors Note
1131 ///
1132 /// This (`Backend` impl overridable) operation dispatches integer exponentiation
1133 /// to [`Self::float_powi_scalar`], and the remaining non-integer exponent cases to
1134 /// the (`Backend` impl overridable) [`Self::float_powf_scalar_impl`]
1135 /// operation to handle the generic case.
1136 ///
1137 /// # Arguments
1138 ///
1139 /// * `tensor` - The tensor to exponentiate.
1140 /// * `value` - The exponent.
1141 ///
1142 /// # Returns
1143 ///
1144 /// A tensor with the same shape as `tensor` with values raised to the power of `value`.
1145 fn float_powf_scalar(tensor: FloatTensor<B>, value: Scalar) -> FloatTensor<B> {
1146 if let Some(exp) = value.try_as_integer() {
1147 Self::float_powi_scalar(tensor, exp)
1148 } else {
1149 Self::float_powf_scalar_impl(tensor, value)
1150 }
1151 }
1152
1153 /// Returns a new tensor with values raised to the power of float `value`.
1154 ///
1155 /// # Backend Implementors Note
1156 ///
1157 /// This is the generic implementation of integer exponentiation
1158 /// called by [`Self::float_powf_scalar`] in the fallback case.
1159 ///
1160 /// This is the minimal required support a `Backend` must implement
1161 /// for exponentiation.
1162 ///
1163 /// As a general rule, this should not be called directly.
1164 ///
1165 /// # Arguments
1166 ///
1167 /// * `tensor` - The tensor to exponentiate.
1168 /// * `value` - The exponent.
1169 ///
1170 /// # Returns
1171 ///
1172 /// A tensor with the same shape as `tensor` with values raised to the power of `value`.
1173 fn float_powf_scalar_impl(tensor: FloatTensor<B>, value: Scalar) -> FloatTensor<B>;
1174
1175 /// Returns a new tensor with square root values.
1176 ///
1177 /// # Arguments
1178 ///
1179 /// * `tensor` - The tensor to take the square root of.
1180 ///
1181 /// # Returns
1182 ///
1183 /// A tensor with the same shape as `tensor` with square root values.
1184 fn float_sqrt(tensor: FloatTensor<B>) -> FloatTensor<B>;
1185
1186 /// Returns a new tensor with the Euclidean distance values.
1187 ///
1188 /// # Arguments
1189 ///
1190 /// * `lhs` - The left-hand side tensor.
1191 /// * `rhs` - The right-hand side tensor.
1192 ///
1193 /// # Returns
1194 ///
1195 /// A tensor with the same shape as `lhs` and `rhs` with hypotenuse values.
1196 fn float_hypot(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B> {
1197 // default implementation for any backend that can't either iterator over elements or doesn't have
1198 // a native hypot implementation
1199
1200 // Mirrors glibc's approach: scale by max(|lhs|, |rhs|) to avoid
1201 // overflow/underflow in the intermediate squaring step.
1202 //
1203 // hypot(x, y) = |max| * sqrt(1 + (min/max)^2)
1204 //
1205 // Edge cases:
1206 // - If max == 0, both inputs are 0, result is 0 (division guarded by clamp)
1207 // - If max is inf, result is inf (propagates naturally through sqrt)
1208 // - NaN propagates naturally
1209 let abs_lhs = B::float_abs(lhs);
1210 let abs_rhs = B::float_abs(rhs);
1211
1212 let diff = B::float_clamp_min(B::float_sub(abs_rhs.clone(), abs_lhs.clone()), 0.0.into());
1213 let max = B::float_add(abs_lhs.clone(), diff.clone());
1214 let min = B::float_sub(abs_rhs, diff);
1215
1216 // Clamp max to at least epsilon to avoid 0/0; result will be 0 anyway
1217 // since min <= max, so (min/clamped_max)^2 won't blow up meaningfully.
1218 let max_safe = B::float_clamp_min(
1219 max.clone(),
1220 max.dtype().finfo().unwrap().min_positive.into(),
1221 );
1222
1223 let ratio = B::float_div(min, max_safe);
1224 let ratio_sq = B::float_mul(ratio.clone(), ratio);
1225
1226 let inner = B::float_add_scalar(ratio_sq, 1.0.into());
1227
1228 B::float_mul(max, B::float_sqrt(inner))
1229 }
1230
1231 /// Returns a new tensor with absolute values.
1232 ///
1233 /// # Arguments
1234 ///
1235 /// * `tensor` - The tensor to take absolute value of.
1236 ///
1237 /// # Returns
1238 ///
1239 /// A tensor with the same shape as `tensor` with absolute values.
1240 fn float_abs(tensor: FloatTensor<B>) -> FloatTensor<B>;
1241
1242 /// Returns a new tensor with cosine values.
1243 ///
1244 /// # Arguments
1245 ///
1246 /// * `tensor` - The tensor to take the cosine of.
1247 ///
1248 /// # Returns
1249 ///
1250 /// A tensor with the same shape as `tensor` with cosine values.
1251 fn float_cos(tensor: FloatTensor<B>) -> FloatTensor<B>;
1252
1253 /// Returns a new tensor with sine values.
1254 ///
1255 /// # Arguments
1256 ///
1257 /// * `tensor` - The tensor to take the sine of.
1258 ///
1259 /// # Returns
1260 ///
1261 /// A tensor with the same shape as `tensor` with sine values.
1262 fn float_sin(tensor: FloatTensor<B>) -> FloatTensor<B>;
1263
1264 /// Returns a new tensor with tangent values.
1265 ///
1266 /// # Arguments
1267 ///
1268 /// * `tensor` - The tensor to take the tangent of.
1269 ///
1270 /// # Returns
1271 ///
1272 /// A tensor with the same shape as `tensor` with tangent values.
1273 fn float_tan(tensor: FloatTensor<B>) -> FloatTensor<B>;
1274
1275 /// Returns a new tensor with hyperbolic cosine values.
1276 ///
1277 /// # Arguments
1278 ///
1279 /// * `tensor` - The tensor to take the hyperbolic cosine of.
1280 ///
1281 /// # Returns
1282 ///
1283 /// A tensor with the same shape as `tensor` with hyperbolic cosine values.
1284 fn float_cosh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1285
1286 /// Returns a new tensor with hyperbolic sine values.
1287 ///
1288 /// # Arguments
1289 ///
1290 /// * `tensor` - The tensor to take the hyperbolic sine of.
1291 ///
1292 /// # Returns
1293 ///
1294 /// A tensor with the same shape as `tensor` with hyperbolic sine values.
1295 fn float_sinh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1296
1297 /// Returns a new tensor with hyperbolic tangent values.
1298 ///
1299 /// # Arguments
1300 ///
1301 /// * `tensor` - The tensor to take the hyperbolic tangent of.
1302 ///
1303 /// # Returns
1304 ///
1305 /// A tensor with the same shape as `tensor` with hyperbolic tangent values.
1306 fn float_tanh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1307
1308 /// Returns a new tensor with inverse cosine values.
1309 ///
1310 /// # Arguments
1311 ///
1312 /// * `tensor` - The input tensor.
1313 ///
1314 /// # Returns
1315 ///
1316 /// A tensor with the same shape as `tensor` with inverse cosine values.
1317 fn float_acos(tensor: FloatTensor<B>) -> FloatTensor<B>;
1318
1319 /// Returns a new tensor with inverse hyperbolic cosine values.
1320 ///
1321 /// # Arguments
1322 ///
1323 /// * `tensor` - The input tensor.
1324 ///
1325 /// # Returns
1326 ///
1327 /// A tensor with the same shape as `tensor` with inverse hyperbolic cosine values.
1328 fn float_acosh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1329
1330 /// Returns a new tensor with inverse sine values.
1331 ///
1332 /// # Arguments
1333 ///
1334 /// * `tensor` - The input tensor.
1335 ///
1336 /// # Returns
1337 ///
1338 /// A tensor with the same shape as `tensor` with inverse sine values.
1339 fn float_asin(tensor: FloatTensor<B>) -> FloatTensor<B>;
1340
1341 /// Returns a new tensor with inverse hyperbolic sine values.
1342 ///
1343 /// # Arguments
1344 ///
1345 /// * `tensor` - The input tensor.
1346 ///
1347 /// # Returns
1348 ///
1349 /// A tensor with the same shape as `tensor` with inverse hyperbolic sine values.
1350 fn float_asinh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1351
1352 /// Returns a new tensor with the inverse tangent values.
1353 ///
1354 /// # Arguments
1355 ///
1356 /// * `tensor` - The input tensor.
1357 ///
1358 /// # Returns
1359 ///
1360 /// A tensor with the same shape as `tensor` with the inverse tangent values.
1361 fn float_atan(tensor: FloatTensor<B>) -> FloatTensor<B>;
1362
1363 /// Returns a new tensor with the inverse hyperbolic tangent values.
1364 ///
1365 /// # Arguments
1366 ///
1367 /// * `tensor` - The input tensor.
1368 ///
1369 /// # Returns
1370 ///
1371 /// A tensor with the same shape as `tensor` with the inverse hyperbolic tangent values.
1372 fn float_atanh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1373
1374 /// Returns a tensor with the four-quadrant inverse tangent values of `y` and `x`.
1375 ///
1376 /// # Arguments
1377 ///
1378 /// * `lhs` - The tensor with y coordinates.
1379 /// * `rhs` - The tensor with x coordinates.
1380 ///
1381 /// # Returns
1382 ///
1383 /// A tensor with the four-quadrant inverse tangent values.
1384 fn float_atan2(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
1385
1386 /// Returns a new tensor with rounded values.
1387 ///
1388 /// This function should implement the [round half to even](https://en.wikipedia.org/wiki/Rounding#Rounding_half_to_even)
1389 /// strategy, with halfway cases rounded to the nearest even integer value.
1390 ///
1391 /// # Arguments
1392 ///
1393 /// * `tensor` - The tensor to be rounded.
1394 ///
1395 /// # Returns
1396 ///
1397 /// A tensor with the same shape as `tensor` with rounded values.
1398 fn float_round(tensor: FloatTensor<B>) -> FloatTensor<B>;
1399
1400 /// Returns a new tensor with floored values.
1401 ///
1402 /// # Arguments
1403 ///
1404 /// * `tensor` - The tensor to be floored.
1405 ///
1406 /// # Returns
1407 ///
1408 /// A tensor with the same shape as `tensor` with floored values.
1409 fn float_floor(tensor: FloatTensor<B>) -> FloatTensor<B>;
1410
1411 /// Returns a new tensor with ceiled values.
1412 ///
1413 /// # Arguments
1414 ///
1415 /// * `tensor` - The tensor to be ceiled.
1416 ///
1417 /// # Returns
1418 ///
1419 /// A tensor with the same shape as `tensor` with ceiled values.
1420 fn float_ceil(tensor: FloatTensor<B>) -> FloatTensor<B>;
1421
1422 /// Returns a new tensor with truncated values.
1423 ///
1424 /// # Arguments
1425 ///
1426 /// * `tensor` - The tensor to be truncated.
1427 ///
1428 /// # Returns
1429 ///
1430 /// A tensor with the same shape as `tensor` with truncated values.
1431 fn float_trunc(tensor: FloatTensor<B>) -> FloatTensor<B>;
1432
1433 /// Returns a new tensor with the error function values.
1434 ///
1435 /// # Arguments
1436 ///
1437 /// * `tensor` - The tensor to take the error function of.
1438 ///
1439 /// # Returns
1440 ///
1441 /// A tensor with the same shape as `tensor` with error function values.
1442 fn float_erf(tensor: FloatTensor<B>) -> FloatTensor<B>;
1443
1444 /// Concatenates tensors along a dimension.
1445 ///
1446 /// # Arguments
1447 ///
1448 /// * `tensors` - The tensors to concatenate.
1449 /// * `dim` - The dimension along which to concatenate.
1450 ///
1451 /// # Returns
1452 ///
1453 /// A tensor with the concatenated tensors along `dim`.
1454 ///
1455 /// # Note
1456 ///
1457 /// Empty tensors (where the concatenation dimension has size 0) are filtered out at the
1458 /// high-level tensor API and will not be passed to this method. Backend implementations do
1459 /// not need to handle empty tensors.
1460 fn float_cat(tensors: Vec<FloatTensor<B>>, dim: usize) -> FloatTensor<B> {
1461 let first_tensor = tensors.first().expect("Tensors should not be empty");
1462 let device = first_tensor.device();
1463
1464 cat_with_slice_assign::<B, _, _, _>(
1465 tensors,
1466 dim,
1467 device,
1468 |shape, device, dtype| B::float_empty(shape, device, dtype.into()),
1469 B::float_slice_assign,
1470 )
1471 }
1472
1473 /// Gets the indices of the maximum elements of a tensor along an axis.
1474 ///
1475 /// # Arguments
1476 ///
1477 /// * `tensor` - The tensor to get the maximum elements of.
1478 /// * `dim` - The dimension along which to get the maximum elements.
1479 /// * `out_dtype` - The output tensor dtype.
1480 ///
1481 /// # Returns
1482 ///
1483 /// A tensor with the indices of the maximum elements of `tensor` along `dim`.
1484 fn float_argmax(tensor: FloatTensor<B>, dim: usize, out_dtype: IntDType) -> IntTensor<B>;
1485
1486 /// Gets the indices of the k maximum elements of a tensor along an axis.
1487 /// if two elements are equals, it will be ordered by lowest indices
1488 ///
1489 /// # Arguments
1490 ///
1491 /// * `tensor` - The tensor to get the maximum elements of.
1492 /// * `dim` - The dimension along which to get the maximum elements.
1493 /// * `k` - number of maximum elements
1494 /// * `out_dtype` - The output tensor dtype.
1495 ///
1496 /// # Returns
1497 ///
1498 /// A tensor with the indices of the maximum elements of `tensor` along `dim`.
1499 fn float_argtopk(
1500 tensor: FloatTensor<B>,
1501 dim: usize,
1502 k: usize,
1503 out_dtype: IntDType,
1504 ) -> IntTensor<B> {
1505 let device = tensor.device();
1506 let dtype = get_device_settings::<B>(&device).int_dtype;
1507 let k_indices = B::int_arange(0..k as i64, &device, dtype);
1508 B::int_select(
1509 Self::float_argsort(tensor, dim, true, out_dtype),
1510 dim,
1511 k_indices,
1512 )
1513 }
1514
1515 /// Gets the values of the k maximum elements of a tensor along an axis.
1516 ///
1517 /// # Arguments
1518 ///
1519 /// * `tensor` - The tensor to get the maximum elements of.
1520 /// * `dim` - The dimension along which to get the maximum elements.
1521 /// * `k` - number of maximum elements
1522 /// * `out_dtype` - The output tensor dtype.
1523 ///
1524 /// # Returns
1525 ///
1526 /// A tensor with the values of the maximum elements of `tensor` along `dim`.
1527 fn float_topk(tensor: FloatTensor<B>, dim: usize, k: usize) -> FloatTensor<B> {
1528 let device = tensor.device();
1529 let dtype = get_device_settings::<B>(&device).int_dtype;
1530 let k_indices = B::int_arange(0..k as i64, &device, dtype);
1531 Self::float_select(Self::float_sort(tensor, dim, true), dim, k_indices)
1532 }
1533
1534 /// Gets the values of the k maximum elements of a tensor along an axis, and their indices.
1535 ///
1536 /// # Arguments
1537 ///
1538 /// * `tensor` - The tensor to get the maximum elements of.
1539 /// * `dim` - The dimension along which to get the maximum elements.
1540 /// * `k` - number of maximum elements
1541 /// * `out_dtype` - The indices tensor dtype.
1542 ///
1543 /// # Returns
1544 ///
1545 /// A tuple with the values of the k maximum elements of `tensor` along `dim`, and their
1546 /// indices.
1547 ///
1548 /// The default sorts once and keeps the first `k` of each half. It deliberately does not
1549 /// compose `float_topk` with `float_argtopk`: those default to a sort each, so that would
1550 /// sort twice, and it would also require `float_argtopk` from backends that only have
1551 /// sorting. Backends whose top-k already carries both results should override this and
1552 /// produce them in a single pass.
1553 fn float_topk_with_indices(
1554 tensor: FloatTensor<B>,
1555 dim: usize,
1556 k: usize,
1557 out_dtype: IntDType,
1558 ) -> (FloatTensor<B>, IntTensor<B>) {
1559 let device = tensor.device();
1560 let dtype = get_device_settings::<B>(&device).int_dtype;
1561 let k_indices = B::int_arange(0..k as i64, &device, dtype);
1562 let (values, indices) = Self::float_sort_with_indices(tensor, dim, true, out_dtype);
1563
1564 (
1565 Self::float_select(values, dim, k_indices.clone()),
1566 B::int_select(indices, dim, k_indices),
1567 )
1568 }
1569
1570 /// Gets the indices of the minimum elements of a tensor along an axis.
1571 ///
1572 /// # Arguments
1573 ///
1574 /// * `tensor` - The tensor to get the minimum elements of.
1575 /// * `dim` - The dimension along which to get the minimum elements.
1576 /// * `out_dtype` - The output tensor dtype.
1577 ///
1578 /// # Returns
1579 ///
1580 /// A tensor with the indices of the minimum elements of `tensor` along `dim`.
1581 fn float_argmin(tensor: FloatTensor<B>, dim: usize, out_dtype: IntDType) -> IntTensor<B>;
1582
1583 /// Gets the maximum element of a tensor.
1584 ///
1585 /// # Arguments
1586 ///
1587 /// * `tensor` - The tensor to get the maximum elements of.
1588 ///
1589 /// # Returns
1590 ///
1591 /// A tensor with the maximum element of `tensor`.
1592 fn float_max(tensor: FloatTensor<B>) -> FloatTensor<B> {
1593 let shape = tensor.shape();
1594 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1595
1596 B::float_max_dim(tensor, 0)
1597 }
1598
1599 /// Gets the maximum elements of a tensor along an axis.
1600 ///
1601 /// # Arguments
1602 ///
1603 /// * `tensor` - The tensor to get the maximum elements of.
1604 /// * `dim` - The dimension along which to get the maximum elements.
1605 ///
1606 /// # Returns
1607 ///
1608 /// A tensor with the maximum elements of `tensor` along `dim`.
1609 fn float_max_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1610 let dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1611 let index = B::float_argmax(tensor.clone(), dim, dtype);
1612
1613 B::float_gather(dim, tensor, index)
1614 }
1615
1616 /// Gets the maximum elements of a tensor along an axis and their indices.
1617 ///
1618 /// # Arguments
1619 ///
1620 /// * `tensor` - The tensor to get the maximum elements of.
1621 /// * `dim` - The dimension along which to get the maximum elements.
1622 /// * `indices_dtype` - The indices tensor dtype.
1623 ///
1624 /// # Returns
1625 ///
1626 /// A tuple with the maximum elements of `tensor` along `dim` and their indices.
1627 fn float_max_dim_with_indices(
1628 tensor: FloatTensor<B>,
1629 dim: usize,
1630 indices_dtype: IntDType,
1631 ) -> (FloatTensor<B>, IntTensor<B>) {
1632 let index = B::float_argmax(tensor.clone(), dim, indices_dtype);
1633 let values = B::float_gather(dim, tensor, index.clone());
1634
1635 (values, index)
1636 }
1637
1638 /// Gets the minimum element of a tensor.
1639 ///
1640 /// # Arguments
1641 ///
1642 /// * `tensor` - The tensor to get the minimum elements of.
1643 ///
1644 /// # Returns
1645 ///
1646 /// A tensor with the minimum element of `tensor`.
1647 fn float_min(tensor: FloatTensor<B>) -> FloatTensor<B> {
1648 let shape = tensor.shape();
1649 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1650
1651 B::float_min_dim(tensor, 0)
1652 }
1653
1654 /// Gets the minimum elements of a tensor along an axis.
1655 ///
1656 /// # Arguments
1657 ///
1658 /// * `tensor` - The tensor to get the minimum elements of.
1659 /// * `dim` - The dimension along which to get the minimum elements.
1660 ///
1661 /// # Returns
1662 ///
1663 /// A tensor with the minimum elements of `tensor` along `dim`.
1664 fn float_min_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1665 let dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1666 let index = B::float_argmin(tensor.clone(), dim, dtype);
1667
1668 B::float_gather(dim, tensor, index)
1669 }
1670
1671 /// Gets the minimum elements of a tensor along an axis and their indices.
1672 ///
1673 /// # Arguments
1674 ///
1675 /// * `tensor` - The tensor to get the minimum elements of.
1676 /// * `dim` - The dimension along which to get the minimum elements.
1677 /// * `indices_dtype` - The indices tensor dtype.
1678 ///
1679 /// # Returns
1680 ///
1681 /// A tuple with the minimum elements of `tensor` along `dim` and their indices.
1682 fn float_min_dim_with_indices(
1683 tensor: FloatTensor<B>,
1684 dim: usize,
1685 indices_dtype: IntDType,
1686 ) -> (FloatTensor<B>, IntTensor<B>) {
1687 let index = B::float_argmin(tensor.clone(), dim, indices_dtype);
1688 let values = B::float_gather(dim, tensor, index.clone());
1689
1690 (values, index)
1691 }
1692
1693 /// Gets the maximum absolute element of a tensor.
1694 ///
1695 /// # Arguments
1696 ///
1697 /// * `tensor` - The tensor to get the maximum elements of.
1698 ///
1699 /// # Returns
1700 ///
1701 /// A tensor with the maximum element of `tensor`.
1702 fn float_max_abs(tensor: FloatTensor<B>) -> FloatTensor<B> {
1703 let shape = tensor.shape();
1704 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1705
1706 B::float_max_abs_dim(tensor, 0)
1707 }
1708
1709 /// Gets the maximum absolute elements of a tensor along an axis.
1710 ///
1711 /// # Arguments
1712 ///
1713 /// * `tensor` - The tensor to get the maximum elements of.
1714 /// * `dim` - The dimension along which to get the maximum elements.
1715 ///
1716 /// # Returns
1717 ///
1718 /// A tensor with the maximum elements of `tensor` along `dim`.
1719 fn float_max_abs_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1720 B::float_max_dim(B::float_abs(tensor), dim)
1721 }
1722
1723 /// Tests if any element in the float `tensor` evaluates to True.
1724 ///
1725 /// # Arguments
1726 ///
1727 /// * `tensor` - The tensor to test.
1728 /// * `out_dtype` - The output tensor dtype.
1729 ///
1730 /// # Returns
1731 ///
1732 /// A boolean tensor with a single element, True if any element in the tensor is True, False otherwise.
1733 fn float_any(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1734 let float_dtype = tensor.dtype();
1735 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1736 let bool_tensor = B::bool_not(bool_tensor);
1737 let sum = B::float_sum(B::bool_into_float(bool_tensor, float_dtype.into()));
1738 B::float_greater_elem(sum, 0f32.into(), out_dtype)
1739 }
1740
1741 /// Tests if any element in the float `tensor` evaluates to True along a given dimension `dim`.
1742 ///
1743 /// # Arguments
1744 ///
1745 /// * `tensor` - The tensor to test.
1746 /// * `dim` - The axis along which to test.
1747 /// * `out_dtype` - The output tensor dtype.
1748 ///
1749 /// # Returns
1750 ///
1751 /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1752 /// where the size is 1. The elem in the `dim` axis is True if any element along this dim in the
1753 /// input evaluates to True, False otherwise.
1754 fn float_any_dim(tensor: FloatTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1755 let float_dtype = tensor.dtype();
1756 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1757 let bool_tensor = B::bool_not(bool_tensor);
1758 let sum = B::float_sum_dim(B::bool_into_float(bool_tensor, float_dtype.into()), dim);
1759 B::float_greater_elem(sum, 0f32.into(), out_dtype)
1760 }
1761
1762 /// Tests if all elements in the float `tensor` evaluate to True.
1763 ///
1764 /// # Arguments
1765 ///
1766 /// * `tensor` - The tensor to test.
1767 /// * `out_dtype` - The output tensor dtype.
1768 ///
1769 /// # Returns
1770 ///
1771 /// A boolean tensor `Tensor<B, 1, Bool>` with a single element, True if all elements in the input tensor
1772 /// evaluate to True, False otherwise.
1773 fn float_all(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1774 let float_dtype = tensor.dtype();
1775 let num_elems = tensor.shape().num_elements() as f32;
1776 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1777 let bool_tensor = B::bool_not(bool_tensor);
1778 let sum = B::float_sum(B::bool_into_float(bool_tensor, float_dtype.into()));
1779 B::float_equal_elem(sum, num_elems.into(), out_dtype)
1780 }
1781
1782 /// Tests if all elements in the float `tensor` evaluate to True along a given dimension `dim`.
1783 ///
1784 /// # Arguments
1785 ///
1786 /// * `tensor` - The tensor to test.
1787 /// * `dim` - The axis along which to test.
1788 /// * `out_dtype` - The output tensor dtype.
1789 ///
1790 /// # Returns
1791 ///
1792 /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1793 /// where the size is 1. The elem in the `dim` axis is True if all elements along this dim in the input
1794 /// evaluates to True, False otherwise.
1795 fn float_all_dim(tensor: FloatTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1796 let float_dtype = tensor.dtype();
1797 let num_elems = tensor.shape()[dim] as f32;
1798 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1799 let bool_tensor = B::bool_not(bool_tensor);
1800 let sum = B::float_sum_dim(B::bool_into_float(bool_tensor, float_dtype.into()), dim);
1801 B::float_equal_elem(sum, num_elems.into(), out_dtype)
1802 }
1803
1804 /// Returns the signs of the float `tensor`.
1805 ///
1806 /// # Arguments
1807 ///
1808 /// * `tensor` - The tensor to extract the signs from.
1809 ///
1810 /// # Returns
1811 ///
1812 /// A tensor with the same shape as `tensor` containing the signs of the elements of `tensor`.
1813 fn float_sign(tensor: FloatTensor<B>) -> FloatTensor<B> {
1814 let device = tensor.device();
1815 let bool_dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
1816 let zeros = B::float_zeros(tensor.shape(), &device, tensor.dtype().into());
1817 let less_than_zero = B::float_lower_elem(tensor.clone(), 0f32.into(), bool_dtype);
1818 let greater_than_zero = B::float_greater_elem(tensor, 0f32.into(), bool_dtype);
1819
1820 let mut result = B::float_mask_fill(zeros, less_than_zero, (-1f32).into());
1821 result = B::float_mask_fill(result, greater_than_zero, 1f32.into());
1822 result
1823 }
1824
1825 /// Broadcasts the float `tensor` to the given `shape`.
1826 fn float_expand(tensor: FloatTensor<B>, shape: Shape) -> FloatTensor<B>;
1827
1828 /// Sort the elements of the input `tensor` by value in along a given dimension.
1829 ///
1830 /// This sort is unstable (i.e., may reorder equal elements).
1831 ///
1832 /// # Arguments
1833 ///
1834 /// * `tensor` - The input tensor.
1835 /// * `dim` - The axis along which to sort.
1836 /// * `descending` - The sorting order.
1837 ///
1838 /// # Returns
1839 ///
1840 /// A tensor with the same shape as the input tensor, where the elements are sorted by value.
1841 fn float_sort(tensor: FloatTensor<B>, dim: usize, descending: bool) -> FloatTensor<B> {
1842 let device = tensor.device();
1843 sort::<B, _, _, _>(
1844 tensor,
1845 dim,
1846 descending,
1847 device,
1848 |tensor| {
1849 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1850 try_read_sync(B::float_into_data(tensor))
1851 .expect(msg)
1852 .expect(msg)
1853 },
1854 |data, device, _dtype| B::float_from_data(data, device),
1855 )
1856 }
1857
1858 /// Sort the elements of the input `tensor` by value in along a given dimension.
1859 ///
1860 /// This sort is unstable (i.e., may reorder equal elements).
1861 ///
1862 /// # Arguments
1863 ///
1864 /// * `tensor` - The input tensor.
1865 /// * `dim` - The axis along which to sort.
1866 /// * `descending` - The sorting order.
1867 /// * `indices_dtype` - The indices tensor dtype.
1868 ///
1869 /// # Returns
1870 ///
1871 /// A tensor with the same shape as the input tensor and corresponding indices, where
1872 /// the elements are sorted by value and the indices map back to the original input tensor.
1873 fn float_sort_with_indices(
1874 tensor: FloatTensor<B>,
1875 dim: usize,
1876 descending: bool,
1877 indices_dtype: IntDType,
1878 ) -> (FloatTensor<B>, IntTensor<B>) {
1879 let device = tensor.device();
1880 sort_with_indices::<B, _, _, _>(
1881 tensor,
1882 dim,
1883 descending,
1884 indices_dtype,
1885 device,
1886 |tensor| {
1887 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1888 try_read_sync(B::float_into_data(tensor))
1889 .expect(msg)
1890 .expect(msg)
1891 },
1892 |data, device, _dtype| B::float_from_data(data, device),
1893 )
1894 }
1895
1896 /// Returns the indices that sort the elements of the input `tensor` by value along a given dimension.
1897 ///
1898 /// This sort is unstable (i.e., may reorder equal elements).
1899 ///
1900 /// # Arguments
1901 ///
1902 /// * `tensor` - The input tensor.
1903 /// * `dim` - The axis along which to sort.
1904 /// * `descending` - The sorting order.
1905 /// * `out_dtype` - The output tensor dtype.
1906 ///
1907 /// # Returns
1908 ///
1909 /// A tensor with the same shape as the input tensor the indices map back to the original input tensor.
1910 fn float_argsort(
1911 tensor: FloatTensor<B>,
1912 dim: usize,
1913 descending: bool,
1914 out_dtype: IntDType,
1915 ) -> IntTensor<B> {
1916 let device = tensor.device();
1917 argsort::<B, _, _>(tensor, dim, descending, out_dtype, device, |tensor| {
1918 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1919 try_read_sync(B::float_into_data(tensor))
1920 .expect(msg)
1921 .expect(msg)
1922 })
1923 }
1924
1925 /// Samples tensor as a two-dimensional spatial grid of (possibly multi-channel) values,
1926 /// using the given locations in [-1, 1].
1927 ///
1928 /// # Arguments
1929 ///
1930 /// * `tensor` - The tensor being sampled from, must be contiguous with shape (N, C, H_in, W_in)
1931 /// * `grid` - A tensor of locations, with shape (N, H_out, W_out, 2). Values are [-1, 1].
1932 /// A [x = -1, y = -1] means top-left, and [x = 1, y = 1] means bottom-right
1933 /// * `options` - Grid sampling options (mode, padding_mode, align_corners)
1934 ///
1935 /// # Returns
1936 ///
1937 /// A tensor with shape (N, C, H_out, W_out)
1938 fn float_grid_sample_2d(
1939 tensor: FloatTensor<B>,
1940 grid: FloatTensor<B>,
1941 options: GridSampleOptions,
1942 ) -> FloatTensor<B> {
1943 // TODO: default impl should get int default dtype
1944 float_grid_sample_2d_ref::<B>(tensor, grid, options)
1945 }
1946
1947 /// Unfold windows along a dimension.
1948 ///
1949 /// Returns a view of the tensor with all complete windows of size `size` in dimension `dim`;
1950 /// where windows are advanced by `step` at each index.
1951 ///
1952 /// The number of windows is `max(0, (shape[dim] - size).ceil_div(step))`.
1953 ///
1954 /// # Arguments
1955 ///
1956 /// * `tensor` - The input tensor to unfold; of shape ``[pre=..., dim shape, post=...]``
1957 /// * `dim` - the selected dim.
1958 /// * `size` - the size of each unfolded window.
1959 /// * `step` - the step between each window.
1960 ///
1961 /// # Returns
1962 ///
1963 /// A tensor view with shape ``[pre=..., windows, size, post=...]``.
1964 fn float_unfold(tensor: FloatTensor<B>, dim: usize, size: usize, step: usize)
1965 -> FloatTensor<B>;
1966
1967 /// Returns a new tensor with boolean elements indicating whether each element of the input is NaN.
1968 ///
1969 /// # Returns
1970 ///
1971 /// A boolean tensor where `true` indicates NaN and `false` indicates a non-NaN value.
1972 fn float_is_nan(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1973 // Check if the input tensor is NaN by comparing it to itself
1974 // NaN is the only value that is not equal to itself
1975 B::float_not_equal(tensor.clone(), tensor, out_dtype)
1976 }
1977
1978 /// Returns a new tensor with boolean elements indicating whether each element of the input is infinite (either +INF or -INF).
1979 ///
1980 /// # Returns
1981 ///
1982 /// A boolean tensor where `true` indicates that the value is infinite
1983 fn float_is_inf(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1984 B::float_equal_elem(B::float_abs(tensor), f64::INFINITY.into(), out_dtype)
1985 }
1986}