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