1use alloc::vec::Vec;
4use burn_backend::{
5 DType, Distribution, ExecutionError, FloatDType, Scalar, TensorData, TensorMetadata,
6 ops::IntTensorOps,
7 tensor::{BoolTensor, Device, FloatTensor, IntTensor},
8};
9use burn_std::{Bytes, IntDType, Shape, Slice, bf16, f16};
10use num_traits::ToPrimitive;
11
12use crate::Layout;
13use crate::ops::binary::{binary_op_typed, int_binary_op, int_scalar_op, scalar_op_typed};
14use crate::{Flex, FlexTensor, ops::matmul};
15
16fn scalar_to_int_pair(dtype: DType, rhs: &Scalar) -> (i64, u64) {
19 if dtype == DType::U64 {
20 (0, rhs.to_u64().unwrap())
21 } else {
22 (rhs.to_i64().unwrap(), 0)
23 }
24}
25
26impl IntTensorOps<Flex> for Flex {
27 fn int_from_data(data: TensorData, _device: &Device<Flex>) -> IntTensor<Flex> {
28 FlexTensor::from_data(data)
29 }
30
31 async fn int_into_data(tensor: IntTensor<Flex>) -> Result<TensorData, ExecutionError> {
32 Ok(tensor.into_data())
33 }
34
35 fn int_to_device(tensor: IntTensor<Flex>, _device: &Device<Flex>) -> IntTensor<Flex> {
36 tensor
37 }
38
39 fn int_cat(tensors: Vec<IntTensor<Flex>>, dim: usize) -> IntTensor<Flex> {
40 crate::ops::cat::cat(tensors, dim)
41 }
42
43 fn int_reshape(tensor: IntTensor<Flex>, shape: Shape) -> IntTensor<Flex> {
44 tensor.reshape(shape)
45 }
46
47 fn int_slice(tensor: IntTensor<Flex>, slices: &[Slice]) -> IntTensor<Flex> {
48 crate::ops::slice::slice(tensor, slices)
49 }
50
51 fn int_empty(shape: Shape, _device: &Device<Flex>, dtype: IntDType) -> IntTensor<Flex> {
52 FlexTensor::empty(shape, dtype.into())
53 }
54
55 fn int_mask_where(
56 tensor: IntTensor<Flex>,
57 mask: BoolTensor<Flex>,
58 value: IntTensor<Flex>,
59 ) -> IntTensor<Flex> {
60 debug_assert_eq!(
61 tensor.dtype(),
62 value.dtype(),
63 "int_mask_where: dtype mismatch"
64 );
65 match tensor.dtype() {
66 DType::I64 => crate::ops::mask::mask_where::<i64>(tensor, mask, value),
67 DType::I32 => crate::ops::mask::mask_where::<i32>(tensor, mask, value),
68 DType::I16 => crate::ops::mask::mask_where::<i16>(tensor, mask, value),
69 DType::I8 => crate::ops::mask::mask_where::<i8>(tensor, mask, value),
70 DType::U64 => crate::ops::mask::mask_where::<u64>(tensor, mask, value),
71 DType::U32 => crate::ops::mask::mask_where::<u32>(tensor, mask, value),
72 DType::U16 => crate::ops::mask::mask_where::<u16>(tensor, mask, value),
73 DType::U8 => crate::ops::mask::mask_where::<u8>(tensor, mask, value),
74 dt => panic!("int_mask_where: unsupported dtype {:?}", dt),
75 }
76 }
77
78 fn int_mask_fill(
79 tensor: IntTensor<Flex>,
80 mask: BoolTensor<Flex>,
81 value: Scalar,
82 ) -> IntTensor<Flex> {
83 match tensor.dtype() {
84 DType::I64 => crate::ops::mask::mask_fill(tensor, mask, value.to_i64().unwrap()),
85 DType::I32 => crate::ops::mask::mask_fill(tensor, mask, value.to_i64().unwrap() as i32),
86 DType::I16 => crate::ops::mask::mask_fill(tensor, mask, value.to_i64().unwrap() as i16),
87 DType::I8 => crate::ops::mask::mask_fill(tensor, mask, value.to_i64().unwrap() as i8),
88 DType::U64 => crate::ops::mask::mask_fill(tensor, mask, value.to_u64().unwrap()),
89 DType::U32 => crate::ops::mask::mask_fill(tensor, mask, value.to_u64().unwrap() as u32),
90 DType::U16 => crate::ops::mask::mask_fill(tensor, mask, value.to_u64().unwrap() as u16),
91 DType::U8 => crate::ops::mask::mask_fill(tensor, mask, value.to_u64().unwrap() as u8),
92 dt => panic!("int_mask_fill: unsupported dtype {:?}", dt),
93 }
94 }
95
96 fn int_slice_assign(
97 tensor: IntTensor<Flex>,
98 slices: &[Slice],
99 value: IntTensor<Flex>,
100 ) -> IntTensor<Flex> {
101 crate::ops::slice::slice_assign(tensor, slices, value)
102 }
103
104 fn int_gather(
112 dim: usize,
113 tensor: IntTensor<Flex>,
114 indices: IntTensor<Flex>,
115 ) -> IntTensor<Flex> {
116 match tensor.dtype() {
117 DType::I64 => crate::ops::gather_scatter::gather::<i64>(tensor, dim, indices),
118 DType::I32 => crate::ops::gather_scatter::gather::<i32>(tensor, dim, indices),
119 DType::I16 => crate::ops::gather_scatter::gather::<i16>(tensor, dim, indices),
120 DType::I8 => crate::ops::gather_scatter::gather::<i8>(tensor, dim, indices),
121 DType::U64 => crate::ops::gather_scatter::gather::<u64>(tensor, dim, indices),
122 DType::U32 => crate::ops::gather_scatter::gather::<u32>(tensor, dim, indices),
123 DType::U16 => crate::ops::gather_scatter::gather::<u16>(tensor, dim, indices),
124 DType::U8 => crate::ops::gather_scatter::gather::<u8>(tensor, dim, indices),
125 dt => panic!("int_gather: unsupported dtype {:?}", dt),
126 }
127 }
128
129 fn int_scatter_add(
135 dim: usize,
136 tensor: IntTensor<Flex>,
137 indices: IntTensor<Flex>,
138 value: IntTensor<Flex>,
139 ) -> IntTensor<Flex> {
140 debug_assert_eq!(
141 tensor.dtype(),
142 value.dtype(),
143 "int_scatter_add: dtype mismatch"
144 );
145 match tensor.dtype() {
146 DType::I64 => {
147 crate::ops::gather_scatter::scatter_add::<i64>(tensor, dim, indices, value)
148 }
149 DType::I32 => {
150 crate::ops::gather_scatter::scatter_add::<i32>(tensor, dim, indices, value)
151 }
152 DType::I16 => {
153 crate::ops::gather_scatter::scatter_add::<i16>(tensor, dim, indices, value)
154 }
155 DType::I8 => crate::ops::gather_scatter::scatter_add::<i8>(tensor, dim, indices, value),
156 DType::U64 => {
157 crate::ops::gather_scatter::scatter_add::<u64>(tensor, dim, indices, value)
158 }
159 DType::U32 => {
160 crate::ops::gather_scatter::scatter_add::<u32>(tensor, dim, indices, value)
161 }
162 DType::U16 => {
163 crate::ops::gather_scatter::scatter_add::<u16>(tensor, dim, indices, value)
164 }
165 DType::U8 => crate::ops::gather_scatter::scatter_add::<u8>(tensor, dim, indices, value),
166 dt => panic!("int_scatter_add: unsupported dtype {:?}", dt),
167 }
168 }
169
170 fn int_scatter_nd(
171 data: IntTensor<Flex>,
172 indices: IntTensor<Flex>,
173 values: IntTensor<Flex>,
174 reduction: burn_backend::tensor::IndexingUpdateOp,
175 ) -> IntTensor<Flex> {
176 match data.dtype() {
177 DType::I64 => {
178 crate::ops::gather_scatter::scatter_nd::<i64>(data, indices, values, reduction)
179 }
180 DType::I32 => {
181 crate::ops::gather_scatter::scatter_nd::<i32>(data, indices, values, reduction)
182 }
183 DType::I16 => {
184 crate::ops::gather_scatter::scatter_nd::<i16>(data, indices, values, reduction)
185 }
186 DType::I8 => {
187 crate::ops::gather_scatter::scatter_nd::<i8>(data, indices, values, reduction)
188 }
189 DType::U64 => {
190 crate::ops::gather_scatter::scatter_nd::<u64>(data, indices, values, reduction)
191 }
192 DType::U32 => {
193 crate::ops::gather_scatter::scatter_nd::<u32>(data, indices, values, reduction)
194 }
195 DType::U16 => {
196 crate::ops::gather_scatter::scatter_nd::<u16>(data, indices, values, reduction)
197 }
198 DType::U8 => {
199 crate::ops::gather_scatter::scatter_nd::<u8>(data, indices, values, reduction)
200 }
201 dt => panic!("int_scatter_nd: unsupported dtype {:?}", dt),
202 }
203 }
204
205 fn int_gather_nd(data: IntTensor<Flex>, indices: IntTensor<Flex>) -> IntTensor<Flex> {
206 match data.dtype() {
207 DType::I64 => crate::ops::gather_scatter::gather_nd::<i64>(data, indices),
208 DType::I32 => crate::ops::gather_scatter::gather_nd::<i32>(data, indices),
209 DType::I16 => crate::ops::gather_scatter::gather_nd::<i16>(data, indices),
210 DType::I8 => crate::ops::gather_scatter::gather_nd::<i8>(data, indices),
211 DType::U64 => crate::ops::gather_scatter::gather_nd::<u64>(data, indices),
212 DType::U32 => crate::ops::gather_scatter::gather_nd::<u32>(data, indices),
213 DType::U16 => crate::ops::gather_scatter::gather_nd::<u16>(data, indices),
214 DType::U8 => crate::ops::gather_scatter::gather_nd::<u8>(data, indices),
215 dt => panic!("int_gather_nd: unsupported dtype {:?}", dt),
216 }
217 }
218
219 fn int_select(
224 tensor: IntTensor<Flex>,
225 dim: usize,
226 indices: IntTensor<Flex>,
227 ) -> IntTensor<Flex> {
228 match tensor.dtype() {
229 DType::I64 => crate::ops::gather_scatter::select::<i64>(tensor, dim, indices),
230 DType::I32 => crate::ops::gather_scatter::select::<i32>(tensor, dim, indices),
231 DType::I16 => crate::ops::gather_scatter::select::<i16>(tensor, dim, indices),
232 DType::I8 => crate::ops::gather_scatter::select::<i8>(tensor, dim, indices),
233 DType::U64 => crate::ops::gather_scatter::select::<u64>(tensor, dim, indices),
234 DType::U32 => crate::ops::gather_scatter::select::<u32>(tensor, dim, indices),
235 DType::U16 => crate::ops::gather_scatter::select::<u16>(tensor, dim, indices),
236 DType::U8 => crate::ops::gather_scatter::select::<u8>(tensor, dim, indices),
237 dt => panic!("int_select: unsupported dtype {:?}", dt),
238 }
239 }
240
241 fn int_select_add(
247 tensor: IntTensor<Flex>,
248 dim: usize,
249 indices: IntTensor<Flex>,
250 value: IntTensor<Flex>,
251 ) -> IntTensor<Flex> {
252 debug_assert_eq!(
253 tensor.dtype(),
254 value.dtype(),
255 "int_select_add: dtype mismatch"
256 );
257 match tensor.dtype() {
258 DType::I64 => {
259 crate::ops::gather_scatter::select_add::<i64>(tensor, dim, indices, value)
260 }
261 DType::I32 => {
262 crate::ops::gather_scatter::select_add::<i32>(tensor, dim, indices, value)
263 }
264 DType::I16 => {
265 crate::ops::gather_scatter::select_add::<i16>(tensor, dim, indices, value)
266 }
267 DType::I8 => crate::ops::gather_scatter::select_add::<i8>(tensor, dim, indices, value),
268 DType::U64 => {
269 crate::ops::gather_scatter::select_add::<u64>(tensor, dim, indices, value)
270 }
271 DType::U32 => {
272 crate::ops::gather_scatter::select_add::<u32>(tensor, dim, indices, value)
273 }
274 DType::U16 => {
275 crate::ops::gather_scatter::select_add::<u16>(tensor, dim, indices, value)
276 }
277 DType::U8 => crate::ops::gather_scatter::select_add::<u8>(tensor, dim, indices, value),
278 dt => panic!("int_select_add: unsupported dtype {:?}", dt),
279 }
280 }
281
282 fn int_equal(
283 lhs: IntTensor<Flex>,
284 rhs: IntTensor<Flex>,
285 out_dtype: burn_std::BoolDType,
286 ) -> BoolTensor<Flex> {
287 crate::ops::comparison::int_equal(lhs, rhs, out_dtype)
288 }
289
290 fn int_equal_elem(
291 lhs: IntTensor<Flex>,
292 rhs: Scalar,
293 out_dtype: burn_std::BoolDType,
294 ) -> BoolTensor<Flex> {
295 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
296 crate::ops::comparison::int_equal_elem(lhs, i, u, out_dtype)
297 }
298
299 fn int_greater(
300 lhs: IntTensor<Flex>,
301 rhs: IntTensor<Flex>,
302 out_dtype: burn_std::BoolDType,
303 ) -> BoolTensor<Flex> {
304 crate::ops::comparison::int_greater(lhs, rhs, out_dtype)
305 }
306
307 fn int_greater_elem(
308 lhs: IntTensor<Flex>,
309 rhs: Scalar,
310 out_dtype: burn_std::BoolDType,
311 ) -> BoolTensor<Flex> {
312 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
313 crate::ops::comparison::int_greater_elem(lhs, i, u, out_dtype)
314 }
315
316 fn int_greater_equal(
317 lhs: IntTensor<Flex>,
318 rhs: IntTensor<Flex>,
319 out_dtype: burn_std::BoolDType,
320 ) -> BoolTensor<Flex> {
321 crate::ops::comparison::int_greater_equal(lhs, rhs, out_dtype)
322 }
323
324 fn int_greater_equal_elem(
325 lhs: IntTensor<Flex>,
326 rhs: Scalar,
327 out_dtype: burn_std::BoolDType,
328 ) -> BoolTensor<Flex> {
329 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
330 crate::ops::comparison::int_greater_equal_elem(lhs, i, u, out_dtype)
331 }
332
333 fn int_lower(
334 lhs: IntTensor<Flex>,
335 rhs: IntTensor<Flex>,
336 out_dtype: burn_std::BoolDType,
337 ) -> BoolTensor<Flex> {
338 crate::ops::comparison::int_lower(lhs, rhs, out_dtype)
339 }
340
341 fn int_lower_elem(
342 lhs: IntTensor<Flex>,
343 rhs: Scalar,
344 out_dtype: burn_std::BoolDType,
345 ) -> BoolTensor<Flex> {
346 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
347 crate::ops::comparison::int_lower_elem(lhs, i, u, out_dtype)
348 }
349
350 fn int_lower_equal(
351 lhs: IntTensor<Flex>,
352 rhs: IntTensor<Flex>,
353 out_dtype: burn_std::BoolDType,
354 ) -> BoolTensor<Flex> {
355 crate::ops::comparison::int_lower_equal(lhs, rhs, out_dtype)
356 }
357
358 fn int_lower_equal_elem(
359 lhs: IntTensor<Flex>,
360 rhs: Scalar,
361 out_dtype: burn_std::BoolDType,
362 ) -> BoolTensor<Flex> {
363 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
364 crate::ops::comparison::int_lower_equal_elem(lhs, i, u, out_dtype)
365 }
366
367 fn int_add(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
368 int_binary_op(lhs, rhs, |a, b| a + b)
369 }
370
371 fn int_add_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
372 if lhs.dtype() == DType::U64 {
373 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| {
374 a.wrapping_add(b)
375 });
376 }
377 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a + b)
378 }
379
380 fn int_sub(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
381 int_binary_op(lhs, rhs, |a, b| a - b)
382 }
383
384 fn int_sub_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
385 if lhs.dtype() == DType::U64 {
386 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| {
387 a.wrapping_sub(b)
388 });
389 }
390 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a - b)
391 }
392
393 fn int_mul(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
394 int_binary_op(lhs, rhs, |a, b| a * b)
395 }
396
397 fn int_mul_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
398 if lhs.dtype() == DType::U64 {
399 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| {
400 a.wrapping_mul(b)
401 });
402 }
403 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a * b)
404 }
405
406 fn int_div(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
407 if lhs.dtype() == DType::U64 {
409 let (lhs, rhs) = crate::ops::expand::broadcast_binary(lhs, rhs);
410 return binary_op_typed(lhs, &rhs, |a: u64, b: u64| a / b);
411 }
412 int_binary_op(lhs, rhs, |a, b| a / b)
413 }
414
415 fn int_div_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
416 if lhs.dtype() == DType::U64 {
417 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a / b);
418 }
419 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a / b)
420 }
421
422 fn int_remainder(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
423 if lhs.dtype() == DType::U64 {
425 let (lhs, rhs) = crate::ops::expand::broadcast_binary(lhs, rhs);
426 return binary_op_typed(lhs, &rhs, |a: u64, b: u64| a % b);
427 }
428 int_binary_op(lhs, rhs, |a, b| ((a % b) + b) % b)
430 }
431
432 fn int_remainder_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
433 if lhs.dtype() == DType::U64 {
434 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a % b);
435 }
436 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| ((a % b) + b) % b)
438 }
439
440 fn int_into_float(
442 tensor: IntTensor<Flex>,
443 out_dtype: burn_std::FloatDType,
444 ) -> FloatTensor<Flex> {
445 let tensor = tensor.to_contiguous();
446 let shape = tensor.layout().shape().clone();
447 let src = tensor.dtype();
448 let out_dt = DType::from(out_dtype);
449
450 macro_rules! read_ints {
453 (|$x:ident| $conv:expr) => {
454 match src {
455 DType::I64 => tensor.storage::<i64>().iter().map(|&$x| $conv).collect(),
456 DType::I32 => tensor.storage::<i32>().iter().map(|&$x| $conv).collect(),
457 DType::I16 => tensor.storage::<i16>().iter().map(|&$x| $conv).collect(),
458 DType::I8 => tensor.storage::<i8>().iter().map(|&$x| $conv).collect(),
459 DType::U64 => tensor.storage::<u64>().iter().map(|&$x| $conv).collect(),
460 DType::U32 => tensor.storage::<u32>().iter().map(|&$x| $conv).collect(),
461 DType::U16 => tensor.storage::<u16>().iter().map(|&$x| $conv).collect(),
462 DType::U8 => tensor.storage::<u8>().iter().map(|&$x| $conv).collect(),
463 _ => panic!("int_into_float: unsupported source dtype {:?}", src),
464 }
465 };
466 }
467
468 match out_dtype {
469 FloatDType::F64 => {
470 let data: Vec<f64> = read_ints!(|x| x as f64);
471 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
472 }
473 FloatDType::F32 | FloatDType::Flex32 => {
474 let data: Vec<f32> = read_ints!(|x| x as f32);
475 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
476 }
477 FloatDType::F16 => {
478 let data: Vec<f16> = read_ints!(|x| f16::from_f32(x as f32));
479 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
480 }
481 FloatDType::BF16 => {
482 let data: Vec<bf16> = read_ints!(|x| bf16::from_f32(x as f32));
483 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
484 }
485 }
486 }
487
488 fn int_swap_dims(tensor: IntTensor<Flex>, dim1: usize, dim2: usize) -> IntTensor<Flex> {
489 tensor.transpose(dim1, dim2)
490 }
491
492 fn int_permute(tensor: IntTensor<Flex>, axes: &[usize]) -> IntTensor<Flex> {
493 tensor.permute(axes)
494 }
495
496 fn int_flip(tensor: IntTensor<Flex>, axes: &[usize]) -> IntTensor<Flex> {
497 crate::ops::flip::flip(tensor, axes)
498 }
499
500 fn int_random(
501 shape: Shape,
502 distribution: Distribution,
503 _device: &Device<Flex>,
504 dtype: IntDType,
505 ) -> IntTensor<Flex> {
506 let mut seed = crate::backend::SEED.lock().unwrap();
507 let mut rng = seed.take().unwrap_or_else(crate::backend::get_seeded_rng);
508 let data = match dtype {
509 IntDType::I64 => TensorData::random::<i64, _, _>(shape, distribution, &mut rng),
510 IntDType::I32 => TensorData::random::<i32, _, _>(shape, distribution, &mut rng),
511 IntDType::I16 => TensorData::random::<i16, _, _>(shape, distribution, &mut rng),
512 IntDType::I8 => TensorData::random::<i8, _, _>(shape, distribution, &mut rng),
513 IntDType::U64 => TensorData::random::<u64, _, _>(shape, distribution, &mut rng),
514 IntDType::U32 => TensorData::random::<u32, _, _>(shape, distribution, &mut rng),
515 IntDType::U16 => TensorData::random::<u16, _, _>(shape, distribution, &mut rng),
516 IntDType::U8 => TensorData::random::<u8, _, _>(shape, distribution, &mut rng),
517 };
518 *seed = Some(rng);
519 FlexTensor::from_data(data)
520 }
521
522 fn int_expand(tensor: IntTensor<Flex>, shape: Shape) -> IntTensor<Flex> {
523 crate::ops::expand::expand(tensor, shape)
524 }
525
526 fn int_matmul(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
527 matmul::int_matmul(lhs, rhs)
528 }
529
530 fn int_sum(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
531 crate::ops::reduce::sum(tensor)
532 }
533
534 fn int_sum_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
535 crate::ops::reduce::sum_dim(tensor, dim)
536 }
537
538 fn int_prod(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
539 crate::ops::reduce::prod(tensor)
540 }
541
542 fn int_prod_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
543 crate::ops::reduce::prod_dim(tensor, dim)
544 }
545
546 fn int_mean_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
547 crate::ops::reduce::mean_dim(tensor, dim)
548 }
549
550 fn int_cumsum(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
551 match tensor.dtype() {
552 DType::I64 => crate::ops::cumulative::cumsum::<i64>(tensor, dim),
553 DType::I32 => crate::ops::cumulative::cumsum::<i32>(tensor, dim),
554 DType::I16 => crate::ops::cumulative::cumsum::<i16>(tensor, dim),
555 DType::I8 => crate::ops::cumulative::cumsum::<i8>(tensor, dim),
556 DType::U64 => crate::ops::cumulative::cumsum::<u64>(tensor, dim),
557 DType::U32 => crate::ops::cumulative::cumsum::<u32>(tensor, dim),
558 DType::U16 => crate::ops::cumulative::cumsum::<u16>(tensor, dim),
559 DType::U8 => crate::ops::cumulative::cumsum::<u8>(tensor, dim),
560 dt => panic!("int_cumsum: unsupported dtype {:?}", dt),
561 }
562 }
563
564 fn int_cumprod(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
565 match tensor.dtype() {
566 DType::I64 => crate::ops::cumulative::cumprod::<i64>(tensor, dim),
567 DType::I32 => crate::ops::cumulative::cumprod::<i32>(tensor, dim),
568 DType::I16 => crate::ops::cumulative::cumprod::<i16>(tensor, dim),
569 DType::I8 => crate::ops::cumulative::cumprod::<i8>(tensor, dim),
570 DType::U64 => crate::ops::cumulative::cumprod::<u64>(tensor, dim),
571 DType::U32 => crate::ops::cumulative::cumprod::<u32>(tensor, dim),
572 DType::U16 => crate::ops::cumulative::cumprod::<u16>(tensor, dim),
573 DType::U8 => crate::ops::cumulative::cumprod::<u8>(tensor, dim),
574 dt => panic!("int_cumprod: unsupported dtype {:?}", dt),
575 }
576 }
577
578 fn int_cummin(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
579 match tensor.dtype() {
580 DType::I64 => crate::ops::cumulative::cummin::<i64>(tensor, dim),
581 DType::I32 => crate::ops::cumulative::cummin::<i32>(tensor, dim),
582 DType::I16 => crate::ops::cumulative::cummin::<i16>(tensor, dim),
583 DType::I8 => crate::ops::cumulative::cummin::<i8>(tensor, dim),
584 DType::U64 => crate::ops::cumulative::cummin::<u64>(tensor, dim),
585 DType::U32 => crate::ops::cumulative::cummin::<u32>(tensor, dim),
586 DType::U16 => crate::ops::cumulative::cummin::<u16>(tensor, dim),
587 DType::U8 => crate::ops::cumulative::cummin::<u8>(tensor, dim),
588 dt => panic!("int_cummin: unsupported dtype {:?}", dt),
589 }
590 }
591
592 fn int_cummax(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
593 match tensor.dtype() {
594 DType::I64 => crate::ops::cumulative::cummax::<i64>(tensor, dim),
595 DType::I32 => crate::ops::cumulative::cummax::<i32>(tensor, dim),
596 DType::I16 => crate::ops::cumulative::cummax::<i16>(tensor, dim),
597 DType::I8 => crate::ops::cumulative::cummax::<i8>(tensor, dim),
598 DType::U64 => crate::ops::cumulative::cummax::<u64>(tensor, dim),
599 DType::U32 => crate::ops::cumulative::cummax::<u32>(tensor, dim),
600 DType::U16 => crate::ops::cumulative::cummax::<u16>(tensor, dim),
601 DType::U8 => crate::ops::cumulative::cummax::<u8>(tensor, dim),
602 dt => panic!("int_cummax: unsupported dtype {:?}", dt),
603 }
604 }
605
606 fn int_argmax(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
607 crate::ops::reduce::argmax(tensor, dim)
608 }
609
610 fn int_argmin(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
611 crate::ops::reduce::argmin(tensor, dim)
612 }
613
614 fn int_abs(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
615 crate::ops::unary::int_abs(tensor)
616 }
617
618 fn bitwise_and(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
619 int_binary_op(lhs, rhs, |a, b| a & b)
620 }
621
622 fn bitwise_and_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
623 if lhs.dtype() == DType::U64 {
624 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a & b);
625 }
626 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a & b)
627 }
628
629 fn bitwise_or(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
630 int_binary_op(lhs, rhs, |a, b| a | b)
631 }
632
633 fn bitwise_or_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
634 if lhs.dtype() == DType::U64 {
635 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a | b);
636 }
637 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a | b)
638 }
639
640 fn bitwise_xor(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
641 int_binary_op(lhs, rhs, |a, b| a ^ b)
642 }
643
644 fn bitwise_xor_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
645 if lhs.dtype() == DType::U64 {
646 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a ^ b);
647 }
648 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a ^ b)
649 }
650
651 fn bitwise_not(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
652 int_scalar_op(tensor, 0, |a, _| !a)
654 }
655
656 fn bitwise_left_shift(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
658 int_binary_op(lhs, rhs, |a, b| a.wrapping_shl(b as u32))
659 }
660
661 fn bitwise_left_shift_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
662 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a.wrapping_shl(b as u32))
663 }
664
665 fn bitwise_right_shift(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
666 int_binary_op(lhs, rhs, |a, b| a.wrapping_shr(b as u32))
667 }
668
669 fn bitwise_right_shift_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
670 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a.wrapping_shr(b as u32))
671 }
672
673 fn int_cast(tensor: IntTensor<Flex>, dtype: IntDType) -> IntTensor<Flex> {
674 let target_dtype: DType = dtype.into();
675
676 if tensor.dtype() == target_dtype {
678 return tensor;
679 }
680
681 let tensor = tensor.to_contiguous();
683 let shape = tensor.layout().shape().clone();
684
685 macro_rules! cast_impl {
687 ($src_type:ty, $dst_type:ty, $dst_dtype:expr) => {{
688 let src: &[$src_type] = tensor.storage();
689 let dst: Vec<$dst_type> = src.iter().map(|&x| x as $dst_type).collect();
690 FlexTensor::new(
691 Bytes::from_elems(dst),
692 Layout::contiguous(shape),
693 $dst_dtype,
694 )
695 }};
696 }
697
698 match (tensor.dtype(), target_dtype) {
700 (DType::I64, DType::I32) => cast_impl!(i64, i32, DType::I32),
702 (DType::I64, DType::I16) => cast_impl!(i64, i16, DType::I16),
703 (DType::I64, DType::I8) => cast_impl!(i64, i8, DType::I8),
704 (DType::I64, DType::U64) => cast_impl!(i64, u64, DType::U64),
705 (DType::I64, DType::U32) => cast_impl!(i64, u32, DType::U32),
706 (DType::I64, DType::U16) => cast_impl!(i64, u16, DType::U16),
707 (DType::I64, DType::U8) => cast_impl!(i64, u8, DType::U8),
708
709 (DType::I32, DType::I64) => cast_impl!(i32, i64, DType::I64),
711 (DType::I32, DType::I16) => cast_impl!(i32, i16, DType::I16),
712 (DType::I32, DType::I8) => cast_impl!(i32, i8, DType::I8),
713 (DType::I32, DType::U64) => cast_impl!(i32, u64, DType::U64),
714 (DType::I32, DType::U32) => cast_impl!(i32, u32, DType::U32),
715 (DType::I32, DType::U16) => cast_impl!(i32, u16, DType::U16),
716 (DType::I32, DType::U8) => cast_impl!(i32, u8, DType::U8),
717
718 (DType::I16, DType::I64) => cast_impl!(i16, i64, DType::I64),
720 (DType::I16, DType::I32) => cast_impl!(i16, i32, DType::I32),
721 (DType::I16, DType::I8) => cast_impl!(i16, i8, DType::I8),
722 (DType::I16, DType::U64) => cast_impl!(i16, u64, DType::U64),
723 (DType::I16, DType::U32) => cast_impl!(i16, u32, DType::U32),
724 (DType::I16, DType::U16) => cast_impl!(i16, u16, DType::U16),
725 (DType::I16, DType::U8) => cast_impl!(i16, u8, DType::U8),
726
727 (DType::I8, DType::I64) => cast_impl!(i8, i64, DType::I64),
729 (DType::I8, DType::I32) => cast_impl!(i8, i32, DType::I32),
730 (DType::I8, DType::I16) => cast_impl!(i8, i16, DType::I16),
731 (DType::I8, DType::U64) => cast_impl!(i8, u64, DType::U64),
732 (DType::I8, DType::U32) => cast_impl!(i8, u32, DType::U32),
733 (DType::I8, DType::U16) => cast_impl!(i8, u16, DType::U16),
734 (DType::I8, DType::U8) => cast_impl!(i8, u8, DType::U8),
735
736 (DType::U64, DType::I64) => cast_impl!(u64, i64, DType::I64),
738 (DType::U64, DType::I32) => cast_impl!(u64, i32, DType::I32),
739 (DType::U64, DType::I16) => cast_impl!(u64, i16, DType::I16),
740 (DType::U64, DType::I8) => cast_impl!(u64, i8, DType::I8),
741 (DType::U64, DType::U32) => cast_impl!(u64, u32, DType::U32),
742 (DType::U64, DType::U16) => cast_impl!(u64, u16, DType::U16),
743 (DType::U64, DType::U8) => cast_impl!(u64, u8, DType::U8),
744
745 (DType::U32, DType::I64) => cast_impl!(u32, i64, DType::I64),
747 (DType::U32, DType::I32) => cast_impl!(u32, i32, DType::I32),
748 (DType::U32, DType::I16) => cast_impl!(u32, i16, DType::I16),
749 (DType::U32, DType::I8) => cast_impl!(u32, i8, DType::I8),
750 (DType::U32, DType::U64) => cast_impl!(u32, u64, DType::U64),
751 (DType::U32, DType::U16) => cast_impl!(u32, u16, DType::U16),
752 (DType::U32, DType::U8) => cast_impl!(u32, u8, DType::U8),
753
754 (DType::U16, DType::I64) => cast_impl!(u16, i64, DType::I64),
756 (DType::U16, DType::I32) => cast_impl!(u16, i32, DType::I32),
757 (DType::U16, DType::I16) => cast_impl!(u16, i16, DType::I16),
758 (DType::U16, DType::I8) => cast_impl!(u16, i8, DType::I8),
759 (DType::U16, DType::U64) => cast_impl!(u16, u64, DType::U64),
760 (DType::U16, DType::U32) => cast_impl!(u16, u32, DType::U32),
761 (DType::U16, DType::U8) => cast_impl!(u16, u8, DType::U8),
762
763 (DType::U8, DType::I64) => cast_impl!(u8, i64, DType::I64),
765 (DType::U8, DType::I32) => cast_impl!(u8, i32, DType::I32),
766 (DType::U8, DType::I16) => cast_impl!(u8, i16, DType::I16),
767 (DType::U8, DType::I8) => cast_impl!(u8, i8, DType::I8),
768 (DType::U8, DType::U64) => cast_impl!(u8, u64, DType::U64),
769 (DType::U8, DType::U32) => cast_impl!(u8, u32, DType::U32),
770 (DType::U8, DType::U16) => cast_impl!(u8, u16, DType::U16),
771
772 _ => panic!(
773 "int_cast: unsupported conversion from {:?} to {:?}",
774 tensor.dtype(),
775 target_dtype
776 ),
777 }
778 }
779
780 fn int_unfold(
781 tensor: IntTensor<Flex>,
782 dim: usize,
783 size: usize,
784 step: usize,
785 ) -> IntTensor<Flex> {
786 crate::ops::unfold::unfold_int(tensor, dim, size, step)
787 }
788
789 fn int_neg(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
790 int_scalar_op(tensor, 0i64, |a, _| a.wrapping_neg())
791 }
792
793 fn int_clamp(tensor: IntTensor<Flex>, min: Scalar, max: Scalar) -> IntTensor<Flex> {
794 if tensor.dtype() == DType::U64 {
795 let min_val = min.to_u64().unwrap();
796 let max_val = max.to_u64().unwrap();
797 return scalar_op_typed(tensor, 0u64, move |x: u64, _| x.clamp(min_val, max_val));
798 }
799 let min_val = min.to_i64().unwrap();
800 let max_val = max.to_i64().unwrap();
801 int_scalar_op(tensor, 0i64, move |x, _| x.clamp(min_val, max_val))
802 }
803
804 fn int_clamp_min(tensor: IntTensor<Flex>, min: Scalar) -> IntTensor<Flex> {
805 if tensor.dtype() == DType::U64 {
806 let min_val = min.to_u64().unwrap();
807 return scalar_op_typed(tensor, 0u64, move |x: u64, _| x.max(min_val));
808 }
809 let min_val = min.to_i64().unwrap();
810 int_scalar_op(tensor, 0i64, move |x, _| x.max(min_val))
811 }
812
813 fn int_clamp_max(tensor: IntTensor<Flex>, max: Scalar) -> IntTensor<Flex> {
814 if tensor.dtype() == DType::U64 {
815 let max_val = max.to_u64().unwrap();
816 return scalar_op_typed(tensor, 0u64, move |x: u64, _| x.min(max_val));
817 }
818 let max_val = max.to_i64().unwrap();
819 int_scalar_op(tensor, 0i64, move |x, _| x.min(max_val))
820 }
821
822 fn int_sign(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
823 if tensor.dtype() == DType::U64 {
824 return scalar_op_typed(tensor, 0u64, |x: u64, _| if x > 0 { 1 } else { 0 });
825 }
826 int_scalar_op(tensor, 0i64, |x, _| {
827 if x > 0 {
828 1
829 } else if x < 0 {
830 -1
831 } else {
832 0
833 }
834 })
835 }
836
837 fn int_mean(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
838 let n = tensor.layout().num_elements();
839 assert!(n > 0, "int_mean: cannot take mean of empty tensor");
840 let dtype = tensor.dtype();
841 let sum_result = crate::ops::reduce::sum(tensor);
842 macro_rules! compute_mean {
844 ($ty:ty) => {{
845 let data: &[$ty] = sum_result.storage();
846 let mean_val = (data[0] as i64 / n as i64) as $ty;
847 FlexTensor::new(
848 Bytes::from_elems(alloc::vec![mean_val]),
849 Layout::contiguous(Shape::from(alloc::vec![1])),
850 dtype,
851 )
852 }};
853 }
854 match dtype {
855 DType::I64 => compute_mean!(i64),
856 DType::I32 => compute_mean!(i32),
857 DType::I16 => compute_mean!(i16),
858 DType::I8 => compute_mean!(i8),
859 other => panic!("int_mean: unsupported dtype {:?}", other),
860 }
861 }
862
863 fn int_max(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
864 crate::ops::reduce::max(tensor)
865 }
866
867 fn int_max_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
868 crate::ops::reduce::max_dim(tensor, dim)
869 }
870
871 fn int_min(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
872 crate::ops::reduce::min(tensor)
873 }
874
875 fn int_min_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
876 crate::ops::reduce::min_dim(tensor, dim)
877 }
878
879 fn int_max_dim_with_indices(
880 tensor: IntTensor<Flex>,
881 dim: usize,
882 ) -> (IntTensor<Flex>, IntTensor<Flex>) {
883 crate::ops::reduce::max_dim_with_indices(tensor, dim)
884 }
885
886 fn int_min_dim_with_indices(
887 tensor: IntTensor<Flex>,
888 dim: usize,
889 ) -> (IntTensor<Flex>, IntTensor<Flex>) {
890 crate::ops::reduce::min_dim_with_indices(tensor, dim)
891 }
892
893 fn int_any(tensor: IntTensor<Flex>, out_dtype: burn_std::BoolDType) -> BoolTensor<Flex> {
894 crate::ops::comparison::any_int(tensor, out_dtype)
895 }
896
897 fn int_any_dim(
898 tensor: IntTensor<Flex>,
899 dim: usize,
900 out_dtype: burn_std::BoolDType,
901 ) -> BoolTensor<Flex> {
902 crate::ops::comparison::any_int_dim(tensor, dim, out_dtype)
903 }
904
905 fn int_all(tensor: IntTensor<Flex>, out_dtype: burn_std::BoolDType) -> BoolTensor<Flex> {
906 crate::ops::comparison::all_int(tensor, out_dtype)
907 }
908
909 fn int_all_dim(
910 tensor: IntTensor<Flex>,
911 dim: usize,
912 out_dtype: burn_std::BoolDType,
913 ) -> BoolTensor<Flex> {
914 crate::ops::comparison::all_int_dim(tensor, dim, out_dtype)
915 }
916
917 fn int_powi(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
918 int_binary_op(lhs, rhs, |a, b| a.wrapping_pow(b as u32))
919 }
920
921 fn int_zeros(shape: Shape, _device: &Device<Flex>, dtype: IntDType) -> IntTensor<Flex> {
922 FlexTensor::zeros(shape, dtype.into())
923 }
924
925 fn int_ones(shape: Shape, _device: &Device<Flex>, dtype: IntDType) -> IntTensor<Flex> {
926 let dt: DType = dtype.into();
927 match dt {
928 DType::I64 => FlexTensor::filled_typed(shape, dt, 1i64),
929 DType::I32 => FlexTensor::filled_typed(shape, dt, 1i32),
930 DType::I16 => FlexTensor::filled_typed(shape, dt, 1i16),
931 DType::I8 => FlexTensor::filled_typed(shape, dt, 1i8),
932 DType::U64 => FlexTensor::filled_typed(shape, dt, 1u64),
933 DType::U32 => FlexTensor::filled_typed(shape, dt, 1u32),
934 DType::U16 => FlexTensor::filled_typed(shape, dt, 1u16),
935 DType::U8 => FlexTensor::filled_typed(shape, dt, 1u8),
936 _ => unreachable!(),
937 }
938 }
939
940 fn int_full(
941 shape: Shape,
942 fill_value: burn_backend::Scalar,
943 _device: &Device<Flex>,
944 dtype: IntDType,
945 ) -> IntTensor<Flex> {
946 let dt: DType = dtype.into();
947 let v = fill_value.to_i64().unwrap();
948 match dt {
949 DType::I64 => FlexTensor::filled_typed(shape, dt, v),
950 DType::I32 => FlexTensor::filled_typed(shape, dt, v as i32),
951 DType::I16 => FlexTensor::filled_typed(shape, dt, v as i16),
952 DType::I8 => FlexTensor::filled_typed(shape, dt, v as i8),
953 DType::U64 => FlexTensor::filled_typed(shape, dt, v as u64),
954 DType::U32 => FlexTensor::filled_typed(shape, dt, v as u32),
955 DType::U16 => FlexTensor::filled_typed(shape, dt, v as u16),
956 DType::U8 => FlexTensor::filled_typed(shape, dt, v as u8),
957 _ => unreachable!(),
958 }
959 }
960
961 fn int_transpose(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
962 let ndims = tensor.layout().num_dims();
963 if ndims < 2 {
964 return tensor;
965 }
966 tensor.transpose(ndims - 2, ndims - 1)
967 }
968
969 fn int_repeat_dim(tensor: IntTensor<Flex>, dim: usize, times: usize) -> IntTensor<Flex> {
970 crate::ops::repeat_dim::repeat_dim(tensor, dim, times)
971 }
972
973 fn int_not_equal(
974 lhs: IntTensor<Flex>,
975 rhs: IntTensor<Flex>,
976 out_dtype: burn_std::BoolDType,
977 ) -> BoolTensor<Flex> {
978 crate::ops::comparison::int_not_equal(lhs, rhs, out_dtype)
979 }
980
981 fn int_not_equal_elem(
982 lhs: IntTensor<Flex>,
983 rhs: burn_backend::Scalar,
984 out_dtype: burn_std::BoolDType,
985 ) -> BoolTensor<Flex> {
986 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
987 crate::ops::comparison::int_not_equal_elem(lhs, i, u, out_dtype)
988 }
989
990 fn int_sort(tensor: IntTensor<Flex>, dim: usize, descending: bool) -> IntTensor<Flex> {
991 crate::ops::sort::sort(tensor, dim, descending)
992 }
993
994 fn int_sort_with_indices(
995 tensor: IntTensor<Flex>,
996 dim: usize,
997 descending: bool,
998 ) -> (IntTensor<Flex>, IntTensor<Flex>) {
999 crate::ops::sort::sort_with_indices(tensor, dim, descending)
1000 }
1001
1002 fn int_argsort(tensor: IntTensor<Flex>, dim: usize, descending: bool) -> IntTensor<Flex> {
1003 crate::ops::sort::argsort(tensor, dim, descending)
1004 }
1005
1006 fn int_powi_scalar(lhs: IntTensor<Flex>, rhs: burn_backend::Scalar) -> IntTensor<Flex> {
1007 use num_traits::ToPrimitive;
1008 match rhs.to_i64().unwrap() {
1009 0 => Self::int_ones(lhs.shape(), &Default::default(), lhs.dtype().into()),
1010 1 => lhs,
1011 2 => Self::int_mul(lhs.clone(), lhs),
1012 _ => Self::int_powi_scalar_impl(lhs, rhs),
1013 }
1014 }
1015
1016 fn int_powi_scalar_impl(lhs: IntTensor<Flex>, rhs: burn_backend::Scalar) -> IntTensor<Flex> {
1017 use num_traits::ToPrimitive;
1018 let exp = rhs.to_i64().unwrap() as u32;
1019 if lhs.dtype() == DType::U64 {
1020 return scalar_op_typed(lhs, exp as u64, move |x: u64, _| x.wrapping_pow(exp));
1021 }
1022 int_scalar_op(lhs, exp as i64, move |x, _| x.wrapping_pow(exp))
1023 }
1024
1025 fn int_max_abs(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
1026 let abs = Self::int_abs(tensor);
1027 crate::ops::reduce::max(abs)
1028 }
1029
1030 fn int_max_abs_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
1031 let abs = Self::int_abs(tensor);
1032 crate::ops::reduce::max_dim(abs, dim)
1033 }
1034
1035 fn int_arange(
1036 range: core::ops::Range<i64>,
1037 _device: &Device<Flex>,
1038 dtype: IntDType,
1039 ) -> IntTensor<Flex> {
1040 Self::int_arange_step(range, 1, &Default::default(), dtype)
1041 }
1042
1043 fn int_arange_step(
1044 range: core::ops::Range<i64>,
1045 step: usize,
1046 _device: &Device<Flex>,
1047 dtype: IntDType,
1048 ) -> IntTensor<Flex> {
1049 let dt: DType = dtype.into();
1050
1051 macro_rules! arange_typed {
1052 ($ty:ty) => {{
1053 let data: Vec<$ty> = range.step_by(step).map(|v| v as $ty).collect();
1054 let shape = Shape::from(alloc::vec![data.len()]);
1055 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), dt)
1056 }};
1057 }
1058
1059 match dt {
1060 DType::I64 => arange_typed!(i64),
1061 DType::I32 => arange_typed!(i32),
1062 DType::I16 => arange_typed!(i16),
1063 DType::I8 => arange_typed!(i8),
1064 DType::U64 => arange_typed!(u64),
1065 DType::U32 => arange_typed!(u32),
1066 DType::U16 => arange_typed!(u16),
1067 DType::U8 => arange_typed!(u8),
1068 _ => unreachable!(),
1069 }
1070 }
1071}
1072
1073#[cfg(test)]
1083mod tests {
1084 use alloc::vec;
1085 use burn_backend::TensorData;
1086 use burn_backend::ops::IntTensorOps;
1087
1088 use crate::Flex;
1089 use crate::FlexTensor;
1090
1091 #[test]
1092 fn test_u64_div_large_values() {
1093 let a = FlexTensor::from_data(TensorData::new(vec![u64::MAX], [1]));
1094 let b = FlexTensor::from_data(TensorData::new(vec![2u64], [1]));
1095 let result = Flex::int_div(a, b);
1096 let values: Vec<u64> = bytemuck::cast_slice(&result.into_data().bytes).to_vec();
1097 assert_eq!(values[0], u64::MAX / 2);
1098 }
1099
1100 #[test]
1101 fn test_u64_remainder_large_values() {
1102 let a = FlexTensor::from_data(TensorData::new(vec![u64::MAX], [1]));
1103 let b = FlexTensor::from_data(TensorData::new(vec![2u64], [1]));
1104 let result = Flex::int_remainder(a, b);
1105 let values: Vec<u64> = bytemuck::cast_slice(&result.into_data().bytes).to_vec();
1106 assert_eq!(values[0], u64::MAX % 2);
1107 }
1108
1109 #[test]
1110 fn test_int_abs_min_value() {
1111 let a = FlexTensor::from_data(TensorData::new(vec![i64::MIN], [1]));
1113 let result = Flex::int_abs(a);
1114 let values: Vec<i64> = bytemuck::cast_slice(&result.into_data().bytes).to_vec();
1115 assert_eq!(values[0], i64::MIN.wrapping_abs());
1116 }
1117
1118 #[test]
1119 fn test_int_neg_min_value() {
1120 let a = FlexTensor::from_data(TensorData::new(vec![i64::MIN], [1]));
1122 let result = Flex::int_neg(a);
1123 let values: Vec<i64> = bytemuck::cast_slice(&result.into_data().bytes).to_vec();
1124 assert_eq!(values[0], i64::MIN.wrapping_neg());
1125 }
1126
1127 #[test]
1128 fn test_int_shift_large_amount() {
1129 let a = FlexTensor::from_data(TensorData::new(vec![1i64], [1]));
1131 let b = FlexTensor::from_data(TensorData::new(vec![64i64], [1]));
1132 let _left = Flex::bitwise_left_shift(a.clone(), b.clone());
1133 let _right = Flex::bitwise_right_shift(a, b);
1134 }
1135
1136 #[test]
1137 fn test_int_into_float_f64() {
1138 use burn_backend::ops::IntTensorOps;
1139 use burn_std::FloatDType;
1140
1141 let t = FlexTensor::from_data(TensorData::new(vec![1i64, 2, -3], [3]));
1142 let result = Flex::int_into_float(t, FloatDType::F64);
1143 assert_eq!(result.dtype(), burn_backend::DType::F64);
1144 let data: Vec<f64> = result.into_data().to_vec().unwrap();
1145 assert_eq!(data, vec![1.0f64, 2.0, -3.0]);
1146 }
1147
1148 #[test]
1149 fn test_u64_add_scalar_large() {
1150 let t = FlexTensor::from_data(TensorData::new(vec![1u64, 2, 3], [3]));
1151 let big: u64 = (i64::MAX as u64) + 100;
1152 let result = Flex::int_add_scalar(t, burn_backend::Scalar::from(big));
1153 let data: Vec<u64> = result.into_data().to_vec().unwrap();
1154 assert_eq!(data, vec![big + 1, big + 2, big + 3]);
1155 }
1156
1157 #[test]
1158 fn test_u64_greater_elem_large() {
1159 let big: u64 = (i64::MAX as u64) + 100;
1160 let t = FlexTensor::from_data(TensorData::new(vec![big, big + 1, big - 1], [3]));
1161 let result = Flex::int_greater_elem(
1162 t,
1163 burn_backend::Scalar::from(big),
1164 burn_std::BoolStore::Native,
1165 );
1166 let data: Vec<bool> = result.into_data().to_vec().unwrap();
1167 assert_eq!(data, vec![false, true, false]);
1168 }
1169
1170 #[test]
1171 fn test_int_mask_fill_i32() {
1172 let t = FlexTensor::from_data(TensorData::new(vec![1i32, 2, 3, 4], [4]));
1173 let mask = FlexTensor::from_data(TensorData::new(vec![true, false, true, false], [4]));
1174 let result = Flex::int_mask_fill(t, mask, burn_backend::Scalar::from(0i64));
1175 let data: Vec<i32> = result.into_data().to_vec().unwrap();
1176 assert_eq!(data, vec![0, 2, 0, 4]);
1177 }
1178
1179 #[test]
1180 fn test_int_mask_fill_i16() {
1181 let t = FlexTensor::from_data(TensorData::new(vec![10i16, 20, 30, 40], [4]));
1182 let mask = FlexTensor::from_data(TensorData::new(vec![false, true, false, true], [4]));
1183 let result = Flex::int_mask_fill(t, mask, burn_backend::Scalar::from(-1i64));
1184 let data: Vec<i16> = result.into_data().to_vec().unwrap();
1185 assert_eq!(data, vec![10, -1, 30, -1]);
1186 }
1187
1188 #[test]
1189 fn test_int_mask_fill_u8() {
1190 let t = FlexTensor::from_data(TensorData::new(vec![1u8, 2, 3, 4], [4]));
1191 let mask = FlexTensor::from_data(TensorData::new(vec![true, true, false, false], [4]));
1192 let result = Flex::int_mask_fill(t, mask, burn_backend::Scalar::from(255i64));
1193 let data: Vec<u8> = result.into_data().to_vec().unwrap();
1194 assert_eq!(data, vec![255, 255, 3, 4]);
1195 }
1196
1197 #[test]
1198 fn test_int_mask_fill_u32() {
1199 let t = FlexTensor::from_data(TensorData::new(vec![100u32, 200, 300], [3]));
1200 let mask = FlexTensor::from_data(TensorData::new(vec![true, false, true], [3]));
1201 let result = Flex::int_mask_fill(t, mask, burn_backend::Scalar::from(0i64));
1202 let data: Vec<u32> = result.into_data().to_vec().unwrap();
1203 assert_eq!(data, vec![0, 200, 0]);
1204 }
1205
1206 #[test]
1207 fn test_int_mask_where_i32() {
1208 let t = FlexTensor::from_data(TensorData::new(vec![1i32, 2, 3, 4], [4]));
1209 let mask = FlexTensor::from_data(TensorData::new(vec![true, false, true, false], [4]));
1210 let v = FlexTensor::from_data(TensorData::new(vec![10i32, 20, 30, 40], [4]));
1211 let result = Flex::int_mask_where(t, mask, v);
1212 let data: Vec<i32> = result.into_data().to_vec().unwrap();
1213 assert_eq!(data, vec![10, 2, 30, 4]);
1214 }
1215
1216 #[test]
1217 fn test_int_mask_where_u8() {
1218 let t = FlexTensor::from_data(TensorData::new(vec![1u8, 2, 3, 4], [4]));
1219 let mask = FlexTensor::from_data(TensorData::new(vec![false, true, false, true], [4]));
1220 let v = FlexTensor::from_data(TensorData::new(vec![10u8, 20, 30, 40], [4]));
1221 let result = Flex::int_mask_where(t, mask, v);
1222 let data: Vec<u8> = result.into_data().to_vec().unwrap();
1223 assert_eq!(data, vec![1, 20, 3, 40]);
1224 }
1225
1226 #[test]
1227 fn test_int_gather_i32() {
1228 let t = FlexTensor::from_data(TensorData::new(vec![10i32, 20, 30, 40, 50, 60], [2, 3]));
1229 let indices = FlexTensor::from_data(TensorData::new(vec![2i64, 0, 1, 2], [2, 2]));
1230 let result = Flex::int_gather(1, t, indices);
1231 let data: Vec<i32> = result.into_data().to_vec().unwrap();
1232 assert_eq!(data, vec![30, 10, 50, 60]);
1233 }
1234
1235 #[test]
1236 fn test_int_select_u16() {
1237 let t = FlexTensor::from_data(TensorData::new(vec![10u16, 20, 30, 40, 50, 60], [2, 3]));
1238 let indices = FlexTensor::from_data(TensorData::new(vec![0i64, 1], [2]));
1239 let result = Flex::int_select(t, 1, indices);
1240 let data: Vec<u16> = result.into_data().to_vec().unwrap();
1241 assert_eq!(data, vec![10, 20, 40, 50]);
1242 }
1243
1244 #[test]
1245 fn test_int_cumsum_i32() {
1246 let t = FlexTensor::from_data(TensorData::new(vec![1i32, 2, 3, 4], [4]));
1247 let result = Flex::int_cumsum(t, 0);
1248 let data: Vec<i32> = result.into_data().to_vec().unwrap();
1249 assert_eq!(data, vec![1, 3, 6, 10]);
1250 }
1251
1252 #[test]
1253 fn test_int_cumprod_u8() {
1254 let t = FlexTensor::from_data(TensorData::new(vec![1u8, 2, 3, 4], [4]));
1255 let result = Flex::int_cumprod(t, 0);
1256 let data: Vec<u8> = result.into_data().to_vec().unwrap();
1257 assert_eq!(data, vec![1, 2, 6, 24]);
1258 }
1259
1260 #[test]
1261 fn test_int_cummin_i32() {
1262 let t = FlexTensor::from_data(TensorData::new(vec![3i32, 1, 4, 1, 5], [5]));
1263 let result = Flex::int_cummin(t, 0);
1264 let data: Vec<i32> = result.into_data().to_vec().unwrap();
1265 assert_eq!(data, vec![3, 1, 1, 1, 1]);
1266 }
1267
1268 #[test]
1269 fn test_int_cummax_u16() {
1270 let t = FlexTensor::from_data(TensorData::new(vec![3u16, 1, 4, 1, 5], [5]));
1271 let result = Flex::int_cummax(t, 0);
1272 let data: Vec<u16> = result.into_data().to_vec().unwrap();
1273 assert_eq!(data, vec![3, 3, 4, 4, 5]);
1274 }
1275
1276 #[test]
1277 fn test_int_scatter_add_i32() {
1278 let t = FlexTensor::from_data(TensorData::new(vec![0i32, 0, 0], [1, 3]));
1279 let indices = FlexTensor::from_data(TensorData::new(vec![0i64, 2, 1], [1, 3]));
1280 let values = FlexTensor::from_data(TensorData::new(vec![10i32, 20, 30], [1, 3]));
1281 let result = Flex::int_scatter_add(1, t, indices, values);
1282 let data: Vec<i32> = result.into_data().to_vec().unwrap();
1283 assert_eq!(data, vec![10, 30, 20]);
1284 }
1285
1286 #[test]
1287 fn test_int_select_add_u8() {
1288 let t = FlexTensor::from_data(TensorData::new(vec![1u8, 2, 3], [3]));
1289 let indices = FlexTensor::from_data(TensorData::new(vec![0i64, 2], [2]));
1290 let values = FlexTensor::from_data(TensorData::new(vec![10u8, 20], [2]));
1291 let result = Flex::int_select_add(t, 0, indices, values);
1292 let data: Vec<u8> = result.into_data().to_vec().unwrap();
1293 assert_eq!(data, vec![11, 2, 23]);
1294 }
1295
1296 #[test]
1297 fn test_int_random_i32() {
1298 use burn_backend::{DType, Distribution, ops::IntTensorOps};
1299 use burn_std::{IntDType, Shape};
1300
1301 let shape = Shape::from(vec![100]);
1302 let dist = Distribution::Uniform(0.0, 10.0);
1303 let device = crate::FlexDevice;
1304 let t = Flex::int_random(shape, dist, &device, IntDType::I32);
1305 assert_eq!(t.dtype(), DType::I32);
1306 let data: Vec<i32> = t.into_data().to_vec().unwrap();
1307 assert!(data.iter().all(|&v| (0..=10).contains(&v)));
1308 }
1309
1310 #[test]
1311 fn test_int_random_u8() {
1312 use burn_backend::{DType, Distribution, ops::IntTensorOps};
1313 use burn_std::{IntDType, Shape};
1314
1315 let shape = Shape::from(vec![50]);
1316 let dist = Distribution::Uniform(0.0, 100.0);
1317 let device = crate::FlexDevice;
1318 let t = Flex::int_random(shape, dist, &device, IntDType::U8);
1319 assert_eq!(t.dtype(), DType::U8);
1320 }
1321
1322 #[test]
1323 fn test_int_mean_i32() {
1324 use burn_backend::{DType, ops::IntTensorOps};
1325
1326 let t = FlexTensor::from_data(TensorData::new(vec![10i32, 20, 30], [3]));
1327 let result = Flex::int_mean(t);
1328 assert_eq!(result.dtype(), DType::I32);
1329 let data: Vec<i32> = result.into_data().to_vec().unwrap();
1330 assert_eq!(data, vec![20]); }
1332}