1use alloc::vec;
4use alloc::vec::Vec;
5use burn_backend::{
6 DType, Distribution, Element, ExecutionError, FloatDType, Scalar, TensorData, TensorMetadata,
7 ops::{FloatTensorOps, GridSampleOptions, IntTensorOps},
8 tensor::{BoolTensor, Device, FloatTensor, IntTensor},
9};
10use burn_std::{Bytes, IntDType, Shape, Slice, bf16, f16};
11use num_traits::{Float, ToPrimitive};
12
13use crate::Layout;
14use crate::ops::binary::{BinaryOp, binary_op, scalar_op};
15use crate::ops::matmul;
16use crate::ops::unary;
17use crate::{Flex, FlexTensor};
18
19#[inline]
21fn remainder_float<T: Float>(a: T, b: T) -> T {
22 let r = a % b;
23 if r != T::zero() && (r < T::zero()) != (b < T::zero()) {
24 r + b
25 } else {
26 r
27 }
28}
29
30fn powi_float<T: Float>(base: T, exponent: i32) -> T {
31 let magnitude = exponent.unsigned_abs() as usize;
33 if exponent < 0 {
34 let reciprocal = num_traits::pow(base, magnitude / 2).recip();
38 let result = reciprocal * reciprocal;
39 if magnitude & 1 == 1 {
40 result / base
41 } else {
42 result
43 }
44 } else {
45 num_traits::pow(base, magnitude)
46 }
47}
48
49impl FloatTensorOps<Flex> for Flex {
50 fn float_from_data(data: TensorData, _device: &Device<Flex>) -> FloatTensor<Flex> {
51 FlexTensor::from_data(data)
52 }
53
54 fn float_random(
55 shape: Shape,
56 distribution: Distribution,
57 _device: &Device<Flex>,
58 dtype: FloatDType,
59 ) -> FloatTensor<Flex> {
60 let mut seed = crate::backend::SEED.lock();
61 let mut rng = seed.take().unwrap_or_else(crate::backend::get_seeded_rng);
62 let data = match dtype {
63 FloatDType::F64 => TensorData::random::<f64, _, _>(shape, distribution, &mut rng),
64 FloatDType::F32 | FloatDType::Flex32 => {
65 TensorData::random::<f32, _, _>(shape, distribution, &mut rng)
66 }
67 FloatDType::F16 => TensorData::random::<f16, _, _>(shape, distribution, &mut rng),
68 FloatDType::BF16 => TensorData::random::<bf16, _, _>(shape, distribution, &mut rng),
69 };
70 *seed = Some(rng);
71 FlexTensor::from_data(data)
72 }
73
74 async fn float_into_data(tensor: FloatTensor<Flex>) -> Result<TensorData, ExecutionError> {
75 Ok(tensor.into_data())
76 }
77
78 fn float_to_device(tensor: FloatTensor<Flex>, _device: &Device<Flex>) -> FloatTensor<Flex> {
79 tensor
81 }
82
83 fn float_detach(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
84 tensor
85 }
86
87 fn float_into_int(tensor: FloatTensor<Flex>, out_dtype: burn_std::IntDType) -> IntTensor<Flex> {
88 let tensor = tensor.to_contiguous();
89 let shape = tensor.layout().shape().clone();
90 let src = tensor.dtype();
91 let out_dt = DType::from(out_dtype);
92
93 macro_rules! read_floats {
95 (|$x:ident| $conv:expr) => {
96 match src {
97 DType::F32 => tensor
98 .storage::<f32>()
99 .iter()
100 .map(|v| {
101 let $x = *v as f64;
102 $conv
103 })
104 .collect(),
105 DType::F64 => tensor
106 .storage::<f64>()
107 .iter()
108 .map(|v| {
109 let $x = *v;
110 $conv
111 })
112 .collect(),
113 DType::F16 => tensor
114 .storage::<f16>()
115 .iter()
116 .map(|v| {
117 let $x = f32::from(*v) as f64;
118 $conv
119 })
120 .collect(),
121 DType::BF16 => tensor
122 .storage::<bf16>()
123 .iter()
124 .map(|v| {
125 let $x = f32::from(*v) as f64;
126 $conv
127 })
128 .collect(),
129 _ => panic!("float_into_int: unsupported source dtype {:?}", src),
130 }
131 };
132 }
133
134 macro_rules! convert {
135 ($int_ty:ty) => {{
136 let data: Vec<$int_ty> = read_floats!(|x| x as $int_ty);
137 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
138 }};
139 }
140
141 match out_dtype {
142 IntDType::I64 => convert!(i64),
143 IntDType::I32 => convert!(i32),
144 IntDType::I16 => convert!(i16),
145 IntDType::I8 => convert!(i8),
146 IntDType::U64 => convert!(u64),
147 IntDType::U32 => convert!(u32),
148 IntDType::U16 => convert!(u16),
149 IntDType::U8 => convert!(u8),
150 }
151 }
152
153 fn float_empty(shape: Shape, _device: &Device<Flex>, dtype: FloatDType) -> FloatTensor<Flex> {
154 FlexTensor::empty(shape, dtype.into())
155 }
156
157 fn float_add(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
158 binary_op(lhs, rhs, |a, b| a + b, |a, b| a + b, Some(BinaryOp::Add))
159 }
160
161 fn float_add_scalar(lhs: FloatTensor<Flex>, rhs: Scalar) -> FloatTensor<Flex> {
162 let rhs_val = rhs.to_f64().unwrap();
163 scalar_op(lhs, rhs_val, |a, b| a + b, |a, b| a + b)
164 }
165
166 fn float_sub(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
167 binary_op(lhs, rhs, |a, b| a - b, |a, b| a - b, Some(BinaryOp::Sub))
168 }
169
170 fn float_sub_scalar(lhs: FloatTensor<Flex>, rhs: Scalar) -> FloatTensor<Flex> {
171 let rhs_val = rhs.to_f64().unwrap();
172 scalar_op(lhs, rhs_val, |a, b| a - b, |a, b| a - b)
173 }
174
175 fn float_mul(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
176 binary_op(lhs, rhs, |a, b| a * b, |a, b| a * b, Some(BinaryOp::Mul))
177 }
178
179 fn float_mul_scalar(lhs: FloatTensor<Flex>, rhs: Scalar) -> FloatTensor<Flex> {
180 let rhs_val = rhs.to_f64().unwrap();
181 scalar_op(lhs, rhs_val, |a, b| a * b, |a, b| a * b)
182 }
183
184 fn float_div(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
185 binary_op(lhs, rhs, |a, b| a / b, |a, b| a / b, Some(BinaryOp::Div))
186 }
187
188 fn float_div_scalar(lhs: FloatTensor<Flex>, rhs: Scalar) -> FloatTensor<Flex> {
189 let rhs_val = rhs.to_f64().unwrap();
190 scalar_op(lhs, rhs_val, |a, b| a / b, |a, b| a / b)
191 }
192
193 fn float_remainder(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
194 binary_op(lhs, rhs, remainder_float, remainder_float, None)
196 }
197
198 fn float_remainder_scalar(lhs: FloatTensor<Flex>, rhs: Scalar) -> FloatTensor<Flex> {
199 scalar_op(lhs, rhs.to_f64().unwrap(), remainder_float, remainder_float)
201 }
202
203 fn float_fmod(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
204 binary_op(lhs, rhs, |a, b| a % b, |a, b| a % b, None)
206 }
207
208 fn float_fmod_scalar(lhs: FloatTensor<Flex>, rhs: Scalar) -> FloatTensor<Flex> {
209 scalar_op(lhs, rhs.to_f64().unwrap(), |a, b| a % b, |a, b| a % b)
210 }
211
212 fn float_matmul(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
213 matmul::matmul(lhs, rhs)
214 }
215
216 fn float_cross(
217 lhs: FloatTensor<Flex>,
218 rhs: FloatTensor<Flex>,
219 dim: usize,
220 ) -> FloatTensor<Flex> {
221 let shape = lhs.layout().shape();
222 let ndims = shape.num_dims();
223 assert_eq!(
224 shape[dim], 3,
225 "cross product requires dimension {} to have size 3, got {}",
226 dim, shape[dim]
227 );
228
229 let make_slices = |idx: usize| -> alloc::vec::Vec<Slice> {
231 (0..ndims)
232 .map(|d| {
233 if d == dim {
234 Slice::new(idx as isize, Some((idx + 1) as isize), 1)
235 } else {
236 Slice::new(0, None, 1)
237 }
238 })
239 .collect()
240 };
241
242 let a0 = Self::float_slice(lhs.clone(), &make_slices(0));
245 let a1 = Self::float_slice(lhs.clone(), &make_slices(1));
246 let a2 = Self::float_slice(lhs, &make_slices(2));
247
248 let b0 = Self::float_slice(rhs.clone(), &make_slices(0));
249 let b1 = Self::float_slice(rhs.clone(), &make_slices(1));
250 let b2 = Self::float_slice(rhs, &make_slices(2));
251
252 let c0 = Self::float_sub(
257 Self::float_mul(a1.clone(), b2.clone()),
258 Self::float_mul(a2.clone(), b1.clone()),
259 );
260 let c1 = Self::float_sub(
261 Self::float_mul(a2, b0.clone()),
262 Self::float_mul(a0.clone(), b2),
263 );
264 let c2 = Self::float_sub(Self::float_mul(a0, b1), Self::float_mul(a1, b0));
265
266 Self::float_cat(vec![c0, c1, c2], dim)
268 }
269
270 fn float_recip(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
271 unary::recip(tensor)
272 }
273
274 fn float_swap_dims(tensor: FloatTensor<Flex>, dim1: usize, dim2: usize) -> FloatTensor<Flex> {
275 tensor.transpose(dim1, dim2)
276 }
277
278 fn float_permute(tensor: FloatTensor<Flex>, axes: &[usize]) -> FloatTensor<Flex> {
279 tensor.permute(axes)
280 }
281
282 fn float_flip(tensor: FloatTensor<Flex>, axes: &[usize]) -> FloatTensor<Flex> {
283 crate::ops::flip::flip(tensor, axes)
284 }
285
286 fn float_cat(tensors: Vec<FloatTensor<Flex>>, dim: usize) -> FloatTensor<Flex> {
287 crate::ops::cat::cat(tensors, dim)
288 }
289
290 fn float_reshape(tensor: FloatTensor<Flex>, shape: Shape) -> FloatTensor<Flex> {
291 tensor.reshape(shape)
292 }
293
294 fn float_gather(
295 dim: usize,
296 tensor: FloatTensor<Flex>,
297 indices: IntTensor<Flex>,
298 ) -> FloatTensor<Flex> {
299 match tensor.dtype() {
300 DType::F32 => crate::ops::gather_scatter::gather::<f32>(tensor, dim, indices),
301 DType::F64 => crate::ops::gather_scatter::gather::<f64>(tensor, dim, indices),
302 DType::F16 => crate::ops::gather_scatter::gather::<f16>(tensor, dim, indices),
303 DType::BF16 => crate::ops::gather_scatter::gather::<bf16>(tensor, dim, indices),
304 _ => panic!("float_gather: unsupported dtype {:?}", tensor.dtype()),
305 }
306 }
307
308 fn float_scatter(
309 dim: usize,
310 tensor: FloatTensor<Flex>,
311 indices: IntTensor<Flex>,
312 value: FloatTensor<Flex>,
313 update: burn_backend::tensor::IndexingUpdateOp,
314 ) -> FloatTensor<Flex> {
315 match update {
316 burn_backend::tensor::IndexingUpdateOp::Assign => match tensor.dtype() {
317 DType::F32 => {
318 crate::ops::gather_scatter::scatter_assign::<f32>(tensor, dim, indices, value)
319 }
320 DType::F64 => {
321 crate::ops::gather_scatter::scatter_assign::<f64>(tensor, dim, indices, value)
322 }
323 DType::F16 => {
324 crate::ops::gather_scatter::scatter_assign::<f16>(tensor, dim, indices, value)
325 }
326 DType::BF16 => {
327 crate::ops::gather_scatter::scatter_assign::<bf16>(tensor, dim, indices, value)
328 }
329 _ => panic!("float_scatter: unsupported dtype {:?}", tensor.dtype()),
330 },
331 burn_backend::tensor::IndexingUpdateOp::Add => match tensor.dtype() {
332 DType::F32 => {
333 crate::ops::gather_scatter::scatter_add::<f32>(tensor, dim, indices, value)
334 }
335 DType::F64 => {
336 crate::ops::gather_scatter::scatter_add::<f64>(tensor, dim, indices, value)
337 }
338 DType::F16 => {
339 crate::ops::gather_scatter::scatter_add::<f16>(tensor, dim, indices, value)
340 }
341 DType::BF16 => {
342 crate::ops::gather_scatter::scatter_add::<bf16>(tensor, dim, indices, value)
343 }
344 _ => panic!("float_scatter: unsupported dtype {:?}", tensor.dtype()),
345 },
346 burn_backend::tensor::IndexingUpdateOp::Mul => match tensor.dtype() {
347 DType::F32 => {
348 crate::ops::gather_scatter::scatter_mul::<f32>(tensor, dim, indices, value)
349 }
350 DType::F64 => {
351 crate::ops::gather_scatter::scatter_mul::<f64>(tensor, dim, indices, value)
352 }
353 DType::F16 => {
354 crate::ops::gather_scatter::scatter_mul::<f16>(tensor, dim, indices, value)
355 }
356 DType::BF16 => {
357 crate::ops::gather_scatter::scatter_mul::<bf16>(tensor, dim, indices, value)
358 }
359 _ => panic!("float_scatter: unsupported dtype {:?}", tensor.dtype()),
360 },
361 burn_backend::tensor::IndexingUpdateOp::Min => match tensor.dtype() {
362 DType::F32 => {
363 crate::ops::gather_scatter::scatter_min::<f32>(tensor, dim, indices, value)
364 }
365 DType::F64 => {
366 crate::ops::gather_scatter::scatter_min::<f64>(tensor, dim, indices, value)
367 }
368 DType::F16 => {
369 crate::ops::gather_scatter::scatter_min::<f16>(tensor, dim, indices, value)
370 }
371 DType::BF16 => {
372 crate::ops::gather_scatter::scatter_min::<bf16>(tensor, dim, indices, value)
373 }
374 _ => panic!("float_scatter: unsupported dtype {:?}", tensor.dtype()),
375 },
376 burn_backend::tensor::IndexingUpdateOp::Max => match tensor.dtype() {
377 DType::F32 => {
378 crate::ops::gather_scatter::scatter_max::<f32>(tensor, dim, indices, value)
379 }
380 DType::F64 => {
381 crate::ops::gather_scatter::scatter_max::<f64>(tensor, dim, indices, value)
382 }
383 DType::F16 => {
384 crate::ops::gather_scatter::scatter_max::<f16>(tensor, dim, indices, value)
385 }
386 DType::BF16 => {
387 crate::ops::gather_scatter::scatter_max::<bf16>(tensor, dim, indices, value)
388 }
389 _ => panic!("float_scatter: unsupported dtype {:?}", tensor.dtype()),
390 },
391 }
392 }
393
394 fn float_scatter_nd(
395 data: FloatTensor<Flex>,
396 indices: IntTensor<Flex>,
397 values: FloatTensor<Flex>,
398 reduction: burn_backend::tensor::IndexingUpdateOp,
399 ) -> FloatTensor<Flex> {
400 match data.dtype() {
401 DType::F32 => {
402 crate::ops::gather_scatter::scatter_nd::<f32>(data, indices, values, reduction)
403 }
404 DType::F64 => {
405 crate::ops::gather_scatter::scatter_nd::<f64>(data, indices, values, reduction)
406 }
407 DType::F16 => {
408 crate::ops::gather_scatter::scatter_nd::<f16>(data, indices, values, reduction)
409 }
410 DType::BF16 => {
411 crate::ops::gather_scatter::scatter_nd::<bf16>(data, indices, values, reduction)
412 }
413 _ => panic!("float_scatter_nd: unsupported dtype {:?}", data.dtype()),
414 }
415 }
416
417 fn float_gather_nd(data: FloatTensor<Flex>, indices: IntTensor<Flex>) -> FloatTensor<Flex> {
418 match data.dtype() {
419 DType::F32 => crate::ops::gather_scatter::gather_nd::<f32>(data, indices),
420 DType::F64 => crate::ops::gather_scatter::gather_nd::<f64>(data, indices),
421 DType::F16 => crate::ops::gather_scatter::gather_nd::<f16>(data, indices),
422 DType::BF16 => crate::ops::gather_scatter::gather_nd::<bf16>(data, indices),
423 _ => panic!("float_gather_nd: unsupported dtype {:?}", data.dtype()),
424 }
425 }
426
427 fn float_select(
428 tensor: FloatTensor<Flex>,
429 dim: usize,
430 indices: IntTensor<Flex>,
431 ) -> FloatTensor<Flex> {
432 match tensor.dtype() {
433 DType::F32 => crate::ops::gather_scatter::select::<f32>(tensor, dim, indices),
434 DType::F64 => crate::ops::gather_scatter::select::<f64>(tensor, dim, indices),
435 DType::F16 => crate::ops::gather_scatter::select::<f16>(tensor, dim, indices),
436 DType::BF16 => crate::ops::gather_scatter::select::<bf16>(tensor, dim, indices),
437 _ => panic!("float_select: unsupported dtype {:?}", tensor.dtype()),
438 }
439 }
440
441 fn float_select_assign(
442 tensor: FloatTensor<Flex>,
443 dim: usize,
444 indices: IntTensor<Flex>,
445 value: FloatTensor<Flex>,
446 update: burn_backend::tensor::IndexingUpdateOp,
447 ) -> FloatTensor<Flex> {
448 match update {
449 burn_backend::tensor::IndexingUpdateOp::Assign => match tensor.dtype() {
450 DType::F32 => {
451 crate::ops::gather_scatter::select_assign::<f32>(tensor, dim, indices, value)
452 }
453 DType::F64 => {
454 crate::ops::gather_scatter::select_assign::<f64>(tensor, dim, indices, value)
455 }
456 DType::F16 => {
457 crate::ops::gather_scatter::select_assign::<f16>(tensor, dim, indices, value)
458 }
459 DType::BF16 => {
460 crate::ops::gather_scatter::select_assign::<bf16>(tensor, dim, indices, value)
461 }
462 _ => panic!(
463 "float_select_assign: unsupported dtype {:?}",
464 tensor.dtype()
465 ),
466 },
467 burn_backend::tensor::IndexingUpdateOp::Add => match tensor.dtype() {
468 DType::F32 => {
469 crate::ops::gather_scatter::select_add::<f32>(tensor, dim, indices, value)
470 }
471 DType::F64 => {
472 crate::ops::gather_scatter::select_add::<f64>(tensor, dim, indices, value)
473 }
474 DType::F16 => {
475 crate::ops::gather_scatter::select_add::<f16>(tensor, dim, indices, value)
476 }
477 DType::BF16 => {
478 crate::ops::gather_scatter::select_add::<bf16>(tensor, dim, indices, value)
479 }
480 _ => panic!(
481 "float_select_assign: unsupported dtype {:?}",
482 tensor.dtype()
483 ),
484 },
485 burn_backend::tensor::IndexingUpdateOp::Mul => match tensor.dtype() {
486 DType::F32 => {
487 crate::ops::gather_scatter::select_mul::<f32>(tensor, dim, indices, value)
488 }
489 DType::F64 => {
490 crate::ops::gather_scatter::select_mul::<f64>(tensor, dim, indices, value)
491 }
492 DType::F16 => {
493 crate::ops::gather_scatter::select_mul::<f16>(tensor, dim, indices, value)
494 }
495 DType::BF16 => {
496 crate::ops::gather_scatter::select_mul::<bf16>(tensor, dim, indices, value)
497 }
498 _ => panic!(
499 "float_select_assign: unsupported dtype {:?}",
500 tensor.dtype()
501 ),
502 },
503 burn_backend::tensor::IndexingUpdateOp::Min => match tensor.dtype() {
504 DType::F32 => {
505 crate::ops::gather_scatter::select_min::<f32>(tensor, dim, indices, value)
506 }
507 DType::F64 => {
508 crate::ops::gather_scatter::select_min::<f64>(tensor, dim, indices, value)
509 }
510 DType::F16 => {
511 crate::ops::gather_scatter::select_min::<f16>(tensor, dim, indices, value)
512 }
513 DType::BF16 => {
514 crate::ops::gather_scatter::select_min::<bf16>(tensor, dim, indices, value)
515 }
516 _ => panic!(
517 "float_select_assign: unsupported dtype {:?}",
518 tensor.dtype()
519 ),
520 },
521 burn_backend::tensor::IndexingUpdateOp::Max => match tensor.dtype() {
522 DType::F32 => {
523 crate::ops::gather_scatter::select_max::<f32>(tensor, dim, indices, value)
524 }
525 DType::F64 => {
526 crate::ops::gather_scatter::select_max::<f64>(tensor, dim, indices, value)
527 }
528 DType::F16 => {
529 crate::ops::gather_scatter::select_max::<f16>(tensor, dim, indices, value)
530 }
531 DType::BF16 => {
532 crate::ops::gather_scatter::select_max::<bf16>(tensor, dim, indices, value)
533 }
534 _ => panic!(
535 "float_select_assign: unsupported dtype {:?}",
536 tensor.dtype()
537 ),
538 },
539 }
540 }
541
542 fn float_slice(tensor: FloatTensor<Flex>, slices: &[Slice]) -> FloatTensor<Flex> {
543 crate::ops::slice::slice(tensor, slices)
544 }
545
546 fn float_slice_assign(
547 tensor: FloatTensor<Flex>,
548 slices: &[Slice],
549 value: FloatTensor<Flex>,
550 ) -> FloatTensor<Flex> {
551 crate::ops::slice::slice_assign(tensor, slices, value)
552 }
553
554 fn float_mask_where(
555 tensor: FloatTensor<Flex>,
556 mask: BoolTensor<Flex>,
557 value: FloatTensor<Flex>,
558 ) -> FloatTensor<Flex> {
559 match tensor.dtype() {
560 DType::F32 => crate::ops::mask::mask_where_f32(tensor, mask, value),
561 DType::F64 => crate::ops::mask::mask_where_f64(tensor, mask, value),
562 DType::F16 => crate::ops::mask::mask_where_f16(tensor, mask, value),
563 DType::BF16 => crate::ops::mask::mask_where_bf16(tensor, mask, value),
564 dtype => panic!("float_mask_where: unsupported dtype {:?}", dtype),
565 }
566 }
567
568 fn float_mask_fill(
569 tensor: FloatTensor<Flex>,
570 mask: BoolTensor<Flex>,
571 value: Scalar,
572 ) -> FloatTensor<Flex> {
573 match tensor.dtype() {
574 DType::F32 => crate::ops::mask::mask_fill_f32(tensor, mask, value.to_f32().unwrap()),
575 DType::F64 => crate::ops::mask::mask_fill_f64(tensor, mask, value.to_f64().unwrap()),
576 DType::F16 => crate::ops::mask::mask_fill_f16(
577 tensor,
578 mask,
579 f16::from_f64(value.to_f64().unwrap()),
580 ),
581 DType::BF16 => crate::ops::mask::mask_fill_bf16(
582 tensor,
583 mask,
584 bf16::from_f64(value.to_f64().unwrap()),
585 ),
586 dtype => panic!("float_mask_fill: unsupported dtype {:?}", dtype),
587 }
588 }
589
590 fn float_equal(
591 lhs: FloatTensor<Flex>,
592 rhs: FloatTensor<Flex>,
593 out_dtype: burn_std::BoolDType,
594 ) -> BoolTensor<Flex> {
595 crate::ops::comparison::equal(lhs, rhs, out_dtype)
596 }
597
598 fn float_equal_elem(
599 lhs: FloatTensor<Flex>,
600 rhs: Scalar,
601 out_dtype: burn_std::BoolDType,
602 ) -> BoolTensor<Flex> {
603 crate::ops::comparison::equal_elem(lhs, rhs.to_f64().unwrap(), out_dtype)
604 }
605
606 fn float_greater(
607 lhs: FloatTensor<Flex>,
608 rhs: FloatTensor<Flex>,
609 out_dtype: burn_std::BoolDType,
610 ) -> BoolTensor<Flex> {
611 crate::ops::comparison::greater(lhs, rhs, out_dtype)
612 }
613
614 fn float_greater_elem(
615 lhs: FloatTensor<Flex>,
616 rhs: Scalar,
617 out_dtype: burn_std::BoolDType,
618 ) -> BoolTensor<Flex> {
619 crate::ops::comparison::greater_elem(lhs, rhs.to_f64().unwrap(), out_dtype)
620 }
621
622 fn float_greater_equal(
623 lhs: FloatTensor<Flex>,
624 rhs: FloatTensor<Flex>,
625 out_dtype: burn_std::BoolDType,
626 ) -> BoolTensor<Flex> {
627 crate::ops::comparison::greater_equal(lhs, rhs, out_dtype)
628 }
629
630 fn float_greater_equal_elem(
631 lhs: FloatTensor<Flex>,
632 rhs: Scalar,
633 out_dtype: burn_std::BoolDType,
634 ) -> BoolTensor<Flex> {
635 crate::ops::comparison::greater_equal_elem(lhs, rhs.to_f64().unwrap(), out_dtype)
636 }
637
638 fn float_lower(
639 lhs: FloatTensor<Flex>,
640 rhs: FloatTensor<Flex>,
641 out_dtype: burn_std::BoolDType,
642 ) -> BoolTensor<Flex> {
643 crate::ops::comparison::lower(lhs, rhs, out_dtype)
644 }
645
646 fn float_lower_elem(
647 lhs: FloatTensor<Flex>,
648 rhs: Scalar,
649 out_dtype: burn_std::BoolDType,
650 ) -> BoolTensor<Flex> {
651 crate::ops::comparison::lower_elem(lhs, rhs.to_f64().unwrap(), out_dtype)
652 }
653
654 fn float_lower_equal(
655 lhs: FloatTensor<Flex>,
656 rhs: FloatTensor<Flex>,
657 out_dtype: burn_std::BoolDType,
658 ) -> BoolTensor<Flex> {
659 crate::ops::comparison::lower_equal(lhs, rhs, out_dtype)
660 }
661
662 fn float_lower_equal_elem(
663 lhs: FloatTensor<Flex>,
664 rhs: Scalar,
665 out_dtype: burn_std::BoolDType,
666 ) -> BoolTensor<Flex> {
667 crate::ops::comparison::lower_equal_elem(lhs, rhs.to_f64().unwrap(), out_dtype)
668 }
669
670 fn float_not_equal(
671 lhs: FloatTensor<Flex>,
672 rhs: FloatTensor<Flex>,
673 out_dtype: burn_std::BoolDType,
674 ) -> BoolTensor<Flex> {
675 crate::ops::comparison::not_equal(lhs, rhs, out_dtype)
676 }
677
678 fn float_not_equal_elem(
679 lhs: FloatTensor<Flex>,
680 rhs: Scalar,
681 out_dtype: burn_std::BoolDType,
682 ) -> BoolTensor<Flex> {
683 crate::ops::comparison::not_equal_elem(lhs, rhs.to_f64().unwrap(), out_dtype)
684 }
685
686 fn float_neg(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
687 unary::unary_op(tensor, |x: f32| -x, |x: f64| -x)
688 }
689
690 fn float_clamp(tensor: FloatTensor<Flex>, min: Scalar, max: Scalar) -> FloatTensor<Flex> {
691 let min32 = min.to_f32().unwrap();
692 let max32 = max.to_f32().unwrap();
693 let min64 = min.to_f64().unwrap();
694 let max64 = max.to_f64().unwrap();
695 unary::unary_op(
699 tensor,
700 move |x: f32| {
701 if min32.is_nan() || max32.is_nan() {
702 f32::NAN
703 } else {
704 x.clamp(min32, max32)
705 }
706 },
707 move |x: f64| {
708 if min64.is_nan() || max64.is_nan() {
709 f64::NAN
710 } else {
711 x.clamp(min64, max64)
712 }
713 },
714 )
715 }
716
717 fn float_clamp_min(tensor: FloatTensor<Flex>, min: Scalar) -> FloatTensor<Flex> {
718 let min32 = min.to_f32().unwrap();
719 let min64 = min.to_f64().unwrap();
720 unary::unary_op(
723 tensor,
724 move |x: f32| if x.is_nan() || x > min32 { x } else { min32 },
725 move |x: f64| if x.is_nan() || x > min64 { x } else { min64 },
726 )
727 }
728
729 fn float_clamp_max(tensor: FloatTensor<Flex>, max: Scalar) -> FloatTensor<Flex> {
730 let max32 = max.to_f32().unwrap();
731 let max64 = max.to_f64().unwrap();
732 unary::unary_op(
735 tensor,
736 move |x: f32| if x.is_nan() || x < max32 { x } else { max32 },
737 move |x: f64| if x.is_nan() || x < max64 { x } else { max64 },
738 )
739 }
740
741 fn float_sign(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
751 unary::unary_op(
752 tensor,
753 |x: f32| {
754 if x.is_nan() || x == 0.0 {
755 0.0
756 } else {
757 libm::copysignf(1.0, x)
758 }
759 },
760 |x: f64| {
761 if x.is_nan() || x == 0.0 {
762 0.0
763 } else {
764 libm::copysign(1.0, x)
765 }
766 },
767 )
768 }
769
770 fn float_mean(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
771 crate::ops::reduce::mean(tensor)
772 }
773
774 fn float_max(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
775 crate::ops::reduce::max(tensor)
776 }
777
778 fn float_max_dim(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
779 crate::ops::reduce::max_dim(tensor, dim)
780 }
781
782 fn float_min(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
783 crate::ops::reduce::min(tensor)
784 }
785
786 fn float_min_dim(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
787 crate::ops::reduce::min_dim(tensor, dim)
788 }
789
790 fn float_max_dim_with_indices(
791 tensor: FloatTensor<Flex>,
792 dim: usize,
793 indices_dtype: burn_std::IntDType,
794 ) -> (FloatTensor<Flex>, IntTensor<Flex>) {
795 let (values, indices) = crate::ops::reduce::max_dim_with_indices(tensor, dim);
796 if indices.dtype() != DType::from(indices_dtype) {
797 (values, Flex::int_cast(indices, indices_dtype))
798 } else {
799 (values, indices)
800 }
801 }
802
803 fn float_min_dim_with_indices(
804 tensor: FloatTensor<Flex>,
805 dim: usize,
806 indices_dtype: burn_std::IntDType,
807 ) -> (FloatTensor<Flex>, IntTensor<Flex>) {
808 let (values, indices) = crate::ops::reduce::min_dim_with_indices(tensor, dim);
809 if indices.dtype() != DType::from(indices_dtype) {
810 (values, Flex::int_cast(indices, indices_dtype))
811 } else {
812 (values, indices)
813 }
814 }
815
816 fn float_any(tensor: FloatTensor<Flex>, out_dtype: burn_std::BoolDType) -> BoolTensor<Flex> {
817 crate::ops::comparison::any_float(tensor, out_dtype)
818 }
819
820 fn float_any_dim(
821 tensor: FloatTensor<Flex>,
822 dim: usize,
823 out_dtype: burn_std::BoolDType,
824 ) -> BoolTensor<Flex> {
825 crate::ops::comparison::any_float_dim(tensor, dim, out_dtype)
826 }
827
828 fn float_all(tensor: FloatTensor<Flex>, out_dtype: burn_std::BoolDType) -> BoolTensor<Flex> {
829 crate::ops::comparison::all_float(tensor, out_dtype)
830 }
831
832 fn float_all_dim(
833 tensor: FloatTensor<Flex>,
834 dim: usize,
835 out_dtype: burn_std::BoolDType,
836 ) -> BoolTensor<Flex> {
837 crate::ops::comparison::all_float_dim(tensor, dim, out_dtype)
838 }
839
840 fn float_sum(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
841 crate::ops::reduce::sum(tensor)
842 }
843
844 fn float_sum_dim(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
845 crate::ops::reduce::sum_dim(tensor, dim)
846 }
847
848 fn float_mean_dim(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
849 crate::ops::reduce::mean_dim(tensor, dim)
850 }
851
852 fn float_prod(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
853 crate::ops::reduce::prod(tensor)
854 }
855
856 fn float_prod_dim(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
857 crate::ops::reduce::prod_dim(tensor, dim)
858 }
859
860 fn float_cumsum(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
861 match tensor.dtype() {
862 DType::F32 => crate::ops::cumulative::cumsum_f32(tensor, dim),
863 DType::F64 => crate::ops::cumulative::cumsum_f64(tensor, dim),
864 DType::F16 => {
865 crate::ops::cumulative::cumsum_half(tensor, dim, f16::to_f32, f16::from_f32)
866 }
867 DType::BF16 => {
868 crate::ops::cumulative::cumsum_half(tensor, dim, bf16::to_f32, bf16::from_f32)
869 }
870 _ => panic!("float_cumsum: unsupported dtype {:?}", tensor.dtype()),
871 }
872 }
873
874 fn float_cumprod(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
875 match tensor.dtype() {
876 DType::F32 => crate::ops::cumulative::cumprod_f32(tensor, dim),
877 DType::F64 => crate::ops::cumulative::cumprod_f64(tensor, dim),
878 DType::F16 => {
879 crate::ops::cumulative::cumprod_half(tensor, dim, f16::to_f32, f16::from_f32)
880 }
881 DType::BF16 => {
882 crate::ops::cumulative::cumprod_half(tensor, dim, bf16::to_f32, bf16::from_f32)
883 }
884 _ => panic!("float_cumprod: unsupported dtype {:?}", tensor.dtype()),
885 }
886 }
887
888 fn float_cummin(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
889 match tensor.dtype() {
890 DType::F32 => crate::ops::cumulative::cummin_f32(tensor, dim),
891 DType::F64 => crate::ops::cumulative::cummin_f64(tensor, dim),
892 DType::F16 => {
893 crate::ops::cumulative::cummin_half(tensor, dim, f16::to_f32, f16::from_f32)
894 }
895 DType::BF16 => {
896 crate::ops::cumulative::cummin_half(tensor, dim, bf16::to_f32, bf16::from_f32)
897 }
898 _ => panic!("float_cummin: unsupported dtype {:?}", tensor.dtype()),
899 }
900 }
901
902 fn float_cummax(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
903 match tensor.dtype() {
904 DType::F32 => crate::ops::cumulative::cummax_f32(tensor, dim),
905 DType::F64 => crate::ops::cumulative::cummax_f64(tensor, dim),
906 DType::F16 => {
907 crate::ops::cumulative::cummax_half(tensor, dim, f16::to_f32, f16::from_f32)
908 }
909 DType::BF16 => {
910 crate::ops::cumulative::cummax_half(tensor, dim, bf16::to_f32, bf16::from_f32)
911 }
912 _ => panic!("float_cummax: unsupported dtype {:?}", tensor.dtype()),
913 }
914 }
915
916 fn float_cast(tensor: FloatTensor<Flex>, dtype: FloatDType) -> FloatTensor<Flex> {
917 use crate::Layout;
918 use burn_std::{Bytes, bf16, f16};
919
920 let src_dtype = tensor.dtype();
921 let target_dtype = DType::from(dtype);
922
923 if src_dtype == target_dtype {
925 return tensor;
926 }
927
928 let tensor = tensor.to_contiguous();
929 let shape = tensor.layout().shape().clone();
930
931 fn cast_slice<Src: Element + bytemuck::Pod, Dst: Element + bytemuck::Pod>(
932 src: &[Src],
933 ) -> Vec<Dst> {
934 use core::any::TypeId;
935 if TypeId::of::<Src>() == TypeId::of::<f64>()
936 && TypeId::of::<Dst>() == TypeId::of::<f16>()
937 {
938 let f64_slice: &[f64] = bytemuck::cast_slice(src);
939 let mut out = vec![Dst::default(); src.len()];
940 let f16_slice: &mut [f16] = bytemuck::cast_slice_mut(&mut out);
941 for (dst, &v) in f16_slice.iter_mut().zip(f64_slice.iter()) {
942 *dst = f16::from_f64(v);
943 }
944 return out;
945 }
946 if TypeId::of::<Src>() == TypeId::of::<f64>()
947 && TypeId::of::<Dst>() == TypeId::of::<bf16>()
948 {
949 let f64_slice: &[f64] = bytemuck::cast_slice(src);
950 let mut out = vec![Dst::default(); src.len()];
951 let bf16_slice: &mut [bf16] = bytemuck::cast_slice_mut(&mut out);
952 for (dst, &v) in bf16_slice.iter_mut().zip(f64_slice.iter()) {
953 *dst = bf16::from_f64(v);
954 }
955 return out;
956 }
957 src.iter().map(|&v| Dst::from_elem(v)).collect()
958 }
959
960 macro_rules! cast_from {
961 ($src_ty:ty) => {{
962 let src: &[$src_ty] = tensor.storage();
963 match target_dtype {
964 DType::F32 => {
965 let result = cast_slice::<$src_ty, f32>(src);
966 FlexTensor::new(
967 Bytes::from_elems(result),
968 Layout::contiguous(shape),
969 DType::F32,
970 )
971 }
972 DType::F64 => {
973 let result = cast_slice::<$src_ty, f64>(src);
974 FlexTensor::new(
975 Bytes::from_elems(result),
976 Layout::contiguous(shape),
977 DType::F64,
978 )
979 }
980 DType::F16 => {
981 let result = cast_slice::<$src_ty, f16>(src);
982 FlexTensor::new(
983 Bytes::from_elems(result),
984 Layout::contiguous(shape),
985 DType::F16,
986 )
987 }
988 DType::BF16 => {
989 let result = cast_slice::<$src_ty, bf16>(src);
990 FlexTensor::new(
991 Bytes::from_elems(result),
992 Layout::contiguous(shape),
993 DType::BF16,
994 )
995 }
996 _ => panic!("float_cast: unsupported target dtype {:?}", target_dtype),
997 }
998 }};
999 }
1000
1001 match src_dtype {
1002 DType::F32 => cast_from!(f32),
1003 DType::F64 => cast_from!(f64),
1004 DType::F16 => cast_from!(f16),
1005 DType::BF16 => cast_from!(bf16),
1006 _ => panic!("float_cast: unsupported source dtype {:?}", src_dtype),
1007 }
1008 }
1009
1010 fn float_exp(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1011 unary::exp(tensor)
1012 }
1013
1014 fn float_log(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1015 unary::log(tensor)
1016 }
1017
1018 fn float_log1p(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1019 unary::log1p(tensor)
1020 }
1021
1022 fn float_powf(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
1023 binary_op(lhs, rhs, |a: f32, b| a.powf(b), |a: f64, b| a.powf(b), None)
1024 }
1025
1026 fn float_powf_scalar_impl(tensor: FloatTensor<Flex>, value: Scalar) -> FloatTensor<Flex> {
1027 let exp = value.to_f64().unwrap();
1028 scalar_op(tensor, exp, |a: f32, b| a.powf(b), |a: f64, b| a.powf(b))
1029 }
1030
1031 fn float_sqrt(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1032 unary::sqrt(tensor)
1033 }
1034
1035 fn float_abs(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1036 unary::abs(tensor)
1037 }
1038
1039 fn float_cos(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1040 unary::cos(tensor)
1041 }
1042
1043 fn float_sin(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1044 unary::sin(tensor)
1045 }
1046
1047 fn float_tan(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1048 unary::tan(tensor)
1049 }
1050
1051 fn float_cosh(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1052 unary::cosh(tensor)
1053 }
1054
1055 fn float_sinh(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1056 unary::sinh(tensor)
1057 }
1058
1059 fn float_tanh(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1060 unary::tanh(tensor)
1061 }
1062
1063 fn float_acos(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1064 unary::acos(tensor)
1065 }
1066
1067 fn float_acosh(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1068 unary::acosh(tensor)
1069 }
1070
1071 fn float_asin(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1072 unary::asin(tensor)
1073 }
1074
1075 fn float_asinh(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1076 unary::asinh(tensor)
1077 }
1078
1079 fn float_atan(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1080 unary::atan(tensor)
1081 }
1082
1083 fn float_atanh(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1084 unary::atanh(tensor)
1085 }
1086
1087 fn float_atan2(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
1088 binary_op(
1089 lhs,
1090 rhs,
1091 |a: f32, b| a.atan2(b),
1092 |a: f64, b| a.atan2(b),
1093 None,
1094 )
1095 }
1096
1097 fn float_round(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1098 unary::round(tensor)
1099 }
1100
1101 fn float_floor(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1102 unary::floor(tensor)
1103 }
1104
1105 fn float_ceil(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1106 unary::ceil(tensor)
1107 }
1108
1109 fn float_trunc(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1110 unary::trunc(tensor)
1111 }
1112
1113 fn float_erf(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1114 unary::erf(tensor)
1115 }
1116
1117 fn float_argmax(
1118 tensor: FloatTensor<Flex>,
1119 dim: usize,
1120 out_dtype: burn_std::IntDType,
1121 ) -> IntTensor<Flex> {
1122 let result = crate::ops::reduce::argmax(tensor, dim);
1123 if result.dtype() != DType::from(out_dtype) {
1124 Flex::int_cast(result, out_dtype)
1125 } else {
1126 result
1127 }
1128 }
1129
1130 fn float_argmin(
1131 tensor: FloatTensor<Flex>,
1132 dim: usize,
1133 out_dtype: burn_std::IntDType,
1134 ) -> IntTensor<Flex> {
1135 let result = crate::ops::reduce::argmin(tensor, dim);
1136 if result.dtype() != DType::from(out_dtype) {
1137 Flex::int_cast(result, out_dtype)
1138 } else {
1139 result
1140 }
1141 }
1142
1143 fn float_expand(tensor: FloatTensor<Flex>, shape: Shape) -> FloatTensor<Flex> {
1144 crate::ops::expand::expand(tensor, shape)
1145 }
1146
1147 fn float_unfold(
1148 tensor: FloatTensor<Flex>,
1149 dim: usize,
1150 size: usize,
1151 step: usize,
1152 ) -> FloatTensor<Flex> {
1153 crate::ops::unfold::unfold(tensor, dim, size, step)
1155 }
1156
1157 fn float_grid_sample_2d(
1158 tensor: FloatTensor<Flex>,
1159 grid: FloatTensor<Flex>,
1160 options: GridSampleOptions,
1161 ) -> FloatTensor<Flex> {
1162 crate::ops::grid_sample::grid_sample_2d(tensor, grid, options)
1163 }
1164
1165 fn float_zeros(shape: Shape, _device: &Device<Flex>, dtype: FloatDType) -> FloatTensor<Flex> {
1166 FlexTensor::zeros(shape, dtype.into())
1167 }
1168
1169 fn float_ones(shape: Shape, _device: &Device<Flex>, dtype: FloatDType) -> FloatTensor<Flex> {
1170 let dt: burn_backend::DType = dtype.into();
1171 match dt {
1172 DType::F32 => FlexTensor::filled_typed(shape, dt, 1.0f32),
1173 DType::F64 => FlexTensor::filled_typed(shape, dt, 1.0f64),
1174 DType::F16 => FlexTensor::filled_typed(shape, dt, f16::ONE),
1175 DType::BF16 => FlexTensor::filled_typed(shape, dt, bf16::ONE),
1176 _ => unreachable!(),
1177 }
1178 }
1179
1180 fn float_full(
1181 shape: Shape,
1182 fill_value: Scalar,
1183 _device: &Device<Flex>,
1184 dtype: FloatDType,
1185 ) -> FloatTensor<Flex> {
1186 let dt: burn_backend::DType = dtype.into();
1187 match dt {
1188 DType::F32 => FlexTensor::filled_typed(shape, dt, fill_value.to_f32().unwrap()),
1189 DType::F64 => FlexTensor::filled_typed(shape, dt, fill_value.to_f64().unwrap()),
1190 DType::F16 => {
1191 FlexTensor::filled_typed(shape, dt, f16::from_f32(fill_value.to_f32().unwrap()))
1192 }
1193 DType::BF16 => {
1194 FlexTensor::filled_typed(shape, dt, bf16::from_f32(fill_value.to_f32().unwrap()))
1195 }
1196 _ => unreachable!(),
1197 }
1198 }
1199
1200 fn float_transpose(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1201 let ndims = tensor.layout().num_dims();
1202 if ndims < 2 {
1203 return tensor;
1204 }
1205 tensor.transpose(ndims - 2, ndims - 1)
1206 }
1207
1208 fn float_repeat_dim(tensor: FloatTensor<Flex>, dim: usize, times: usize) -> FloatTensor<Flex> {
1209 crate::ops::repeat_dim::repeat_dim(tensor, dim, times)
1210 }
1211
1212 fn float_sort(tensor: FloatTensor<Flex>, dim: usize, descending: bool) -> FloatTensor<Flex> {
1213 crate::ops::sort::sort(tensor, dim, descending)
1214 }
1215
1216 fn float_sort_with_indices(
1217 tensor: FloatTensor<Flex>,
1218 dim: usize,
1219 descending: bool,
1220 indices_dtype: burn_std::IntDType,
1221 ) -> (FloatTensor<Flex>, IntTensor<Flex>) {
1222 let (values, indices) = crate::ops::sort::sort_with_indices(tensor, dim, descending);
1223 let indices = if indices.dtype() != DType::from(indices_dtype) {
1224 Flex::int_cast(indices, indices_dtype)
1225 } else {
1226 indices
1227 };
1228 (values, indices)
1229 }
1230
1231 fn float_argsort(
1232 tensor: FloatTensor<Flex>,
1233 dim: usize,
1234 descending: bool,
1235 out_dtype: burn_std::IntDType,
1236 ) -> IntTensor<Flex> {
1237 let indices = crate::ops::sort::argsort(tensor, dim, descending);
1238 if indices.dtype() != DType::from(out_dtype) {
1239 Flex::int_cast(indices, out_dtype)
1240 } else {
1241 indices
1242 }
1243 }
1244
1245 fn float_powi(lhs: FloatTensor<Flex>, rhs: IntTensor<Flex>) -> FloatTensor<Flex> {
1246 let dtype = lhs.dtype();
1247 Self::float_powf(lhs, Flex::int_into_float(rhs, dtype.into()))
1248 }
1249
1250 fn float_powi_scalar(lhs: FloatTensor<Flex>, rhs: Scalar) -> FloatTensor<Flex> {
1251 match rhs.to_i64().unwrap() {
1252 0 => Self::float_ones(lhs.shape(), &Default::default(), lhs.dtype().into()),
1253 1 => lhs,
1254 2 => Self::float_mul(lhs.clone(), lhs),
1255 -1 => Self::float_recip(lhs),
1256 -2 => Self::float_recip(Self::float_mul(lhs.clone(), lhs)),
1257 _ => Self::float_powi_scalar_impl(lhs, rhs),
1258 }
1259 }
1260
1261 fn float_powi_scalar_impl(lhs: FloatTensor<Flex>, rhs: Scalar) -> FloatTensor<Flex> {
1262 let exponent = rhs.to_f64().unwrap();
1263 if f64::from(exponent as i32) != exponent {
1264 return Self::float_powf_scalar_impl(lhs, rhs);
1265 }
1266 let exponent = exponent as i32;
1267 unary::unary_op(
1268 lhs,
1269 |x| powi_float(x, exponent),
1270 |x| powi_float(x, exponent),
1271 )
1272 }
1273
1274 fn float_powf_scalar(tensor: FloatTensor<Flex>, value: Scalar) -> FloatTensor<Flex> {
1275 if let Some(exp) = value.try_as_integer() {
1276 Self::float_powi_scalar(tensor, exp)
1277 } else {
1278 Self::float_powf_scalar_impl(tensor, value)
1279 }
1280 }
1281
1282 fn float_max_abs(tensor: FloatTensor<Flex>) -> FloatTensor<Flex> {
1283 let abs = unary::abs(tensor);
1284 crate::ops::reduce::max(abs)
1285 }
1286
1287 fn float_max_abs_dim(tensor: FloatTensor<Flex>, dim: usize) -> FloatTensor<Flex> {
1288 let abs = unary::abs(tensor);
1289 crate::ops::reduce::max_dim(abs, dim)
1290 }
1291
1292 fn float_is_nan(tensor: FloatTensor<Flex>, out_dtype: burn_std::BoolDType) -> BoolTensor<Flex> {
1293 unary::float_predicate(tensor, out_dtype, |x: f32| x.is_nan(), |x: f64| x.is_nan())
1294 }
1295
1296 fn float_is_inf(tensor: FloatTensor<Flex>, out_dtype: burn_std::BoolDType) -> BoolTensor<Flex> {
1297 unary::float_predicate(
1298 tensor,
1299 out_dtype,
1300 |x: f32| x.is_infinite(),
1301 |x: f64| x.is_infinite(),
1302 )
1303 }
1304
1305 fn float_hypot(lhs: FloatTensor<Flex>, rhs: FloatTensor<Flex>) -> FloatTensor<Flex> {
1306 binary_op(
1307 lhs,
1308 rhs,
1309 |a: f32, b| a.hypot(b),
1310 |a: f64, b| a.hypot(b),
1311 None,
1312 )
1313 }
1314}
1315
1316#[cfg(test)]
1325mod tests {
1326 use burn_backend::TensorData;
1327
1328 use crate::Flex;
1329
1330 #[test]
1331 fn powi_scalar_preserves_f32_subnormal() {
1332 use burn_backend::ops::FloatTensorOps;
1333
1334 let tensor = crate::FlexTensor::from_data(TensorData::from([2.0f32]));
1335 Flex::float_powi_scalar(tensor, (-149).into())
1336 .into_data()
1337 .assert_eq(&TensorData::from([f32::from_bits(1)]), true);
1338 }
1339
1340 #[test]
1341 fn powi_scalar_preserves_f64_subnormal() {
1342 use burn_backend::ops::FloatTensorOps;
1343
1344 let tensor = crate::FlexTensor::from_data(TensorData::from([2.0f64]));
1345 Flex::float_powi_scalar(tensor, (-1074).into())
1346 .into_data()
1347 .assert_eq(&TensorData::from([f64::from_bits(1)]), true);
1348 }
1349
1350 #[test]
1351 fn test_float_into_int_i32() {
1352 use burn_backend::ops::FloatTensorOps;
1353 use burn_std::IntDType;
1354
1355 let t = crate::FlexTensor::from_data(TensorData::from([1.5f32, -2.7, 0.0, 255.9]));
1356 let result = Flex::float_into_int(t, IntDType::I32);
1357 assert_eq!(result.dtype(), burn_backend::DType::I32);
1358 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1359 assert_eq!(data, vec![1, -2, 0, 255]);
1360 }
1361
1362 #[test]
1363 fn test_float_into_int_u8() {
1364 use burn_backend::ops::FloatTensorOps;
1365 use burn_std::IntDType;
1366
1367 let t = crate::FlexTensor::from_data(TensorData::from([0.0f32, 1.9, 127.5, 255.0]));
1368 let result = Flex::float_into_int(t, IntDType::U8);
1369 assert_eq!(result.dtype(), burn_backend::DType::U8);
1370 let data: Vec<u8> = result.into_data().try_into_vec().unwrap();
1371 assert_eq!(data, vec![0, 1, 127, 255]);
1372 }
1373
1374 #[test]
1375 fn test_float_argmax_i32_out_dtype() {
1376 use burn_backend::ops::FloatTensorOps;
1377 use burn_std::IntDType;
1378
1379 let t = crate::FlexTensor::from_data(TensorData::from([[1.0f32, 3.0, 2.0]]));
1380 let result = Flex::float_argmax(t, 1, IntDType::I32);
1381 assert_eq!(result.dtype(), burn_backend::DType::I32);
1382 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1383 assert_eq!(data, vec![1]);
1384 }
1385
1386 #[test]
1387 fn test_float_argmin_i32_out_dtype() {
1388 use burn_backend::ops::FloatTensorOps;
1389 use burn_std::IntDType;
1390
1391 let t = crate::FlexTensor::from_data(TensorData::from([[3.0f32, 1.0, 2.0]]));
1392 let result = Flex::float_argmin(t, 1, IntDType::I32);
1393 assert_eq!(result.dtype(), burn_backend::DType::I32);
1394 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1395 assert_eq!(data, vec![1]);
1396 }
1397
1398 #[test]
1399 fn test_float_argmax_i64_out_dtype() {
1400 use burn_backend::ops::FloatTensorOps;
1401 use burn_std::IntDType;
1402
1403 let t = crate::FlexTensor::from_data(TensorData::from([[1.0f32, 3.0, 2.0]]));
1404 let result = Flex::float_argmax(t, 1, IntDType::I64);
1405 assert_eq!(result.dtype(), burn_backend::DType::I64);
1406 let data: Vec<i64> = result.into_data().try_into_vec().unwrap();
1407 assert_eq!(data, vec![1]);
1408 }
1409
1410 #[test]
1411 fn test_float_max_dim_with_indices_i32() {
1412 use burn_backend::ops::FloatTensorOps;
1413 use burn_std::IntDType;
1414
1415 let t = crate::FlexTensor::from_data(TensorData::from([[1.0f32, 5.0], [3.0, 2.0]]));
1416 let (values, indices) = Flex::float_max_dim_with_indices(t, 1, IntDType::I32);
1417 assert_eq!(indices.dtype(), burn_backend::DType::I32);
1418 let idx: Vec<i32> = indices.into_data().try_into_vec().unwrap();
1419 assert_eq!(idx, vec![1, 0]);
1420 let vals: Vec<f32> = values.into_data().try_into_vec().unwrap();
1421 assert_eq!(vals, vec![5.0, 3.0]);
1422 }
1423
1424 #[test]
1425 fn test_float_min_dim_with_indices_i32() {
1426 use burn_backend::ops::FloatTensorOps;
1427 use burn_std::IntDType;
1428
1429 let t = crate::FlexTensor::from_data(TensorData::from([[1.0f32, 5.0], [3.0, 2.0]]));
1430 let (values, indices) = Flex::float_min_dim_with_indices(t, 1, IntDType::I32);
1431 assert_eq!(indices.dtype(), burn_backend::DType::I32);
1432 let idx: Vec<i32> = indices.into_data().try_into_vec().unwrap();
1433 assert_eq!(idx, vec![0, 1]);
1434 let vals: Vec<f32> = values.into_data().try_into_vec().unwrap();
1435 assert_eq!(vals, vec![1.0, 2.0]);
1436 }
1437
1438 #[test]
1439 fn test_float_random_f64() {
1440 use burn_backend::{DType, FloatDType, ops::FloatTensorOps};
1441
1442 let shape = burn_std::Shape::from(vec![100]);
1443 let dist = burn_backend::Distribution::Uniform(0.0, 1.0);
1444 let device = crate::FlexDevice;
1445 let t = Flex::float_random(shape, dist, &device, FloatDType::F64);
1446 assert_eq!(t.dtype(), DType::F64);
1447 let data: Vec<f64> = t.into_data().try_into_vec().unwrap();
1448 assert!(data.iter().all(|&v| (0.0..=1.0).contains(&v)));
1449 }
1450
1451 #[test]
1452 fn test_float_random_f16() {
1453 use burn_backend::{DType, FloatDType, ops::FloatTensorOps};
1454
1455 let shape = burn_std::Shape::from(vec![100]);
1456 let dist = burn_backend::Distribution::Uniform(0.0, 1.0);
1457 let device = crate::FlexDevice;
1458 let t = Flex::float_random(shape, dist, &device, FloatDType::F16);
1459 assert_eq!(t.dtype(), DType::F16);
1460 }
1461}