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