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
16#[inline]
18fn remainder_int(a: i64, b: i64) -> i64 {
19 let r = a.wrapping_rem(b);
20 if r != 0 && (r < 0) != (b < 0) {
21 r.wrapping_add(b)
22 } else {
23 r
24 }
25}
26
27fn scalar_to_int_pair(dtype: DType, rhs: &Scalar) -> (i64, u64) {
30 if dtype == DType::U64 {
31 (0, rhs.to_u64().unwrap())
32 } else {
33 (rhs.to_i64().unwrap(), 0)
34 }
35}
36
37impl IntTensorOps<Flex> for Flex {
38 fn int_from_data(data: TensorData, _device: &Device<Flex>) -> IntTensor<Flex> {
39 FlexTensor::from_data(data)
40 }
41
42 async fn int_into_data(tensor: IntTensor<Flex>) -> Result<TensorData, ExecutionError> {
43 Ok(tensor.into_data())
44 }
45
46 fn int_to_device(tensor: IntTensor<Flex>, _device: &Device<Flex>) -> IntTensor<Flex> {
47 tensor
48 }
49
50 fn int_cat(tensors: Vec<IntTensor<Flex>>, dim: usize) -> IntTensor<Flex> {
51 crate::ops::cat::cat(tensors, dim)
52 }
53
54 fn int_reshape(tensor: IntTensor<Flex>, shape: Shape) -> IntTensor<Flex> {
55 tensor.reshape(shape)
56 }
57
58 fn int_slice(tensor: IntTensor<Flex>, slices: &[Slice]) -> IntTensor<Flex> {
59 crate::ops::slice::slice(tensor, slices)
60 }
61
62 fn int_empty(shape: Shape, _device: &Device<Flex>, dtype: IntDType) -> IntTensor<Flex> {
63 FlexTensor::empty(shape, dtype.into())
64 }
65
66 fn int_mask_where(
67 tensor: IntTensor<Flex>,
68 mask: BoolTensor<Flex>,
69 value: IntTensor<Flex>,
70 ) -> IntTensor<Flex> {
71 debug_assert_eq!(
72 tensor.dtype(),
73 value.dtype(),
74 "int_mask_where: dtype mismatch"
75 );
76 match tensor.dtype() {
77 DType::I64 => crate::ops::mask::mask_where::<i64>(tensor, mask, value),
78 DType::I32 => crate::ops::mask::mask_where::<i32>(tensor, mask, value),
79 DType::I16 => crate::ops::mask::mask_where::<i16>(tensor, mask, value),
80 DType::I8 => crate::ops::mask::mask_where::<i8>(tensor, mask, value),
81 DType::U64 => crate::ops::mask::mask_where::<u64>(tensor, mask, value),
82 DType::U32 => crate::ops::mask::mask_where::<u32>(tensor, mask, value),
83 DType::U16 => crate::ops::mask::mask_where::<u16>(tensor, mask, value),
84 DType::U8 => crate::ops::mask::mask_where::<u8>(tensor, mask, value),
85 dt => panic!("int_mask_where: unsupported dtype {:?}", dt),
86 }
87 }
88
89 fn int_mask_fill(
90 tensor: IntTensor<Flex>,
91 mask: BoolTensor<Flex>,
92 value: Scalar,
93 ) -> IntTensor<Flex> {
94 match tensor.dtype() {
95 DType::I64 => crate::ops::mask::mask_fill(tensor, mask, value.to_i64().unwrap()),
96 DType::I32 => crate::ops::mask::mask_fill(tensor, mask, value.to_i64().unwrap() as i32),
97 DType::I16 => crate::ops::mask::mask_fill(tensor, mask, value.to_i64().unwrap() as i16),
98 DType::I8 => crate::ops::mask::mask_fill(tensor, mask, value.to_i64().unwrap() as i8),
99 DType::U64 => crate::ops::mask::mask_fill(tensor, mask, value.to_u64().unwrap()),
100 DType::U32 => crate::ops::mask::mask_fill(tensor, mask, value.to_u64().unwrap() as u32),
101 DType::U16 => crate::ops::mask::mask_fill(tensor, mask, value.to_u64().unwrap() as u16),
102 DType::U8 => crate::ops::mask::mask_fill(tensor, mask, value.to_u64().unwrap() as u8),
103 dt => panic!("int_mask_fill: unsupported dtype {:?}", dt),
104 }
105 }
106
107 fn int_slice_assign(
108 tensor: IntTensor<Flex>,
109 slices: &[Slice],
110 value: IntTensor<Flex>,
111 ) -> IntTensor<Flex> {
112 crate::ops::slice::slice_assign(tensor, slices, value)
113 }
114
115 fn int_gather(
123 dim: usize,
124 tensor: IntTensor<Flex>,
125 indices: IntTensor<Flex>,
126 ) -> IntTensor<Flex> {
127 match tensor.dtype() {
128 DType::I64 => crate::ops::gather_scatter::gather::<i64>(tensor, dim, indices),
129 DType::I32 => crate::ops::gather_scatter::gather::<i32>(tensor, dim, indices),
130 DType::I16 => crate::ops::gather_scatter::gather::<i16>(tensor, dim, indices),
131 DType::I8 => crate::ops::gather_scatter::gather::<i8>(tensor, dim, indices),
132 DType::U64 => crate::ops::gather_scatter::gather::<u64>(tensor, dim, indices),
133 DType::U32 => crate::ops::gather_scatter::gather::<u32>(tensor, dim, indices),
134 DType::U16 => crate::ops::gather_scatter::gather::<u16>(tensor, dim, indices),
135 DType::U8 => crate::ops::gather_scatter::gather::<u8>(tensor, dim, indices),
136 dt => panic!("int_gather: unsupported dtype {:?}", dt),
137 }
138 }
139
140 fn int_scatter(
141 dim: usize,
142 tensor: IntTensor<Flex>,
143 indices: IntTensor<Flex>,
144 value: IntTensor<Flex>,
145 update: burn_backend::tensor::IndexingUpdateOp,
146 ) -> IntTensor<Flex> {
147 match update {
148 burn_backend::tensor::IndexingUpdateOp::Assign => {
149 debug_assert_eq!(tensor.dtype(), value.dtype(), "int_scatter: dtype mismatch");
150 match tensor.dtype() {
151 DType::I64 => crate::ops::gather_scatter::scatter_assign::<i64>(
152 tensor, dim, indices, value,
153 ),
154 DType::I32 => crate::ops::gather_scatter::scatter_assign::<i32>(
155 tensor, dim, indices, value,
156 ),
157 DType::I16 => crate::ops::gather_scatter::scatter_assign::<i16>(
158 tensor, dim, indices, value,
159 ),
160 DType::I8 => crate::ops::gather_scatter::scatter_assign::<i8>(
161 tensor, dim, indices, value,
162 ),
163 DType::U64 => crate::ops::gather_scatter::scatter_assign::<u64>(
164 tensor, dim, indices, value,
165 ),
166 DType::U32 => crate::ops::gather_scatter::scatter_assign::<u32>(
167 tensor, dim, indices, value,
168 ),
169 DType::U16 => crate::ops::gather_scatter::scatter_assign::<u16>(
170 tensor, dim, indices, value,
171 ),
172 DType::U8 => crate::ops::gather_scatter::scatter_assign::<u8>(
173 tensor, dim, indices, value,
174 ),
175 dt => panic!("int_scatter: unsupported dtype {:?}", dt),
176 }
177 }
178 burn_backend::tensor::IndexingUpdateOp::Add => {
179 debug_assert_eq!(tensor.dtype(), value.dtype(), "int_scatter: dtype mismatch");
180 match tensor.dtype() {
181 DType::I64 => {
182 crate::ops::gather_scatter::scatter_add::<i64>(tensor, dim, indices, value)
183 }
184 DType::I32 => {
185 crate::ops::gather_scatter::scatter_add::<i32>(tensor, dim, indices, value)
186 }
187 DType::I16 => {
188 crate::ops::gather_scatter::scatter_add::<i16>(tensor, dim, indices, value)
189 }
190 DType::I8 => {
191 crate::ops::gather_scatter::scatter_add::<i8>(tensor, dim, indices, value)
192 }
193 DType::U64 => {
194 crate::ops::gather_scatter::scatter_add::<u64>(tensor, dim, indices, value)
195 }
196 DType::U32 => {
197 crate::ops::gather_scatter::scatter_add::<u32>(tensor, dim, indices, value)
198 }
199 DType::U16 => {
200 crate::ops::gather_scatter::scatter_add::<u16>(tensor, dim, indices, value)
201 }
202 DType::U8 => {
203 crate::ops::gather_scatter::scatter_add::<u8>(tensor, dim, indices, value)
204 }
205 dt => panic!("int_scatter: unsupported dtype {:?}", dt),
206 }
207 }
208 burn_backend::tensor::IndexingUpdateOp::Mul => {
209 debug_assert_eq!(tensor.dtype(), value.dtype(), "int_scatter: dtype mismatch");
210 match tensor.dtype() {
211 DType::I64 => {
212 crate::ops::gather_scatter::scatter_mul::<i64>(tensor, dim, indices, value)
213 }
214 DType::I32 => {
215 crate::ops::gather_scatter::scatter_mul::<i32>(tensor, dim, indices, value)
216 }
217 DType::I16 => {
218 crate::ops::gather_scatter::scatter_mul::<i16>(tensor, dim, indices, value)
219 }
220 DType::I8 => {
221 crate::ops::gather_scatter::scatter_mul::<i8>(tensor, dim, indices, value)
222 }
223 DType::U64 => {
224 crate::ops::gather_scatter::scatter_mul::<u64>(tensor, dim, indices, value)
225 }
226 DType::U32 => {
227 crate::ops::gather_scatter::scatter_mul::<u32>(tensor, dim, indices, value)
228 }
229 DType::U16 => {
230 crate::ops::gather_scatter::scatter_mul::<u16>(tensor, dim, indices, value)
231 }
232 DType::U8 => {
233 crate::ops::gather_scatter::scatter_mul::<u8>(tensor, dim, indices, value)
234 }
235 dt => panic!("int_scatter: unsupported dtype {:?}", dt),
236 }
237 }
238 burn_backend::tensor::IndexingUpdateOp::Min => {
239 debug_assert_eq!(tensor.dtype(), value.dtype(), "int_scatter: dtype mismatch");
240 match tensor.dtype() {
241 DType::I64 => {
242 crate::ops::gather_scatter::scatter_min::<i64>(tensor, dim, indices, value)
243 }
244 DType::I32 => {
245 crate::ops::gather_scatter::scatter_min::<i32>(tensor, dim, indices, value)
246 }
247 DType::I16 => {
248 crate::ops::gather_scatter::scatter_min::<i16>(tensor, dim, indices, value)
249 }
250 DType::I8 => {
251 crate::ops::gather_scatter::scatter_min::<i8>(tensor, dim, indices, value)
252 }
253 DType::U64 => {
254 crate::ops::gather_scatter::scatter_min::<u64>(tensor, dim, indices, value)
255 }
256 DType::U32 => {
257 crate::ops::gather_scatter::scatter_min::<u32>(tensor, dim, indices, value)
258 }
259 DType::U16 => {
260 crate::ops::gather_scatter::scatter_min::<u16>(tensor, dim, indices, value)
261 }
262 DType::U8 => {
263 crate::ops::gather_scatter::scatter_min::<u8>(tensor, dim, indices, value)
264 }
265 dt => panic!("int_scatter: unsupported dtype {:?}", dt),
266 }
267 }
268 burn_backend::tensor::IndexingUpdateOp::Max => {
269 debug_assert_eq!(tensor.dtype(), value.dtype(), "int_scatter: dtype mismatch");
270 match tensor.dtype() {
271 DType::I64 => {
272 crate::ops::gather_scatter::scatter_max::<i64>(tensor, dim, indices, value)
273 }
274 DType::I32 => {
275 crate::ops::gather_scatter::scatter_max::<i32>(tensor, dim, indices, value)
276 }
277 DType::I16 => {
278 crate::ops::gather_scatter::scatter_max::<i16>(tensor, dim, indices, value)
279 }
280 DType::I8 => {
281 crate::ops::gather_scatter::scatter_max::<i8>(tensor, dim, indices, value)
282 }
283 DType::U64 => {
284 crate::ops::gather_scatter::scatter_max::<u64>(tensor, dim, indices, value)
285 }
286 DType::U32 => {
287 crate::ops::gather_scatter::scatter_max::<u32>(tensor, dim, indices, value)
288 }
289 DType::U16 => {
290 crate::ops::gather_scatter::scatter_max::<u16>(tensor, dim, indices, value)
291 }
292 DType::U8 => {
293 crate::ops::gather_scatter::scatter_max::<u8>(tensor, dim, indices, value)
294 }
295 dt => panic!("int_scatter: unsupported dtype {:?}", dt),
296 }
297 }
298 }
299 }
300
301 fn int_scatter_nd(
302 data: IntTensor<Flex>,
303 indices: IntTensor<Flex>,
304 values: IntTensor<Flex>,
305 reduction: burn_backend::tensor::IndexingUpdateOp,
306 ) -> IntTensor<Flex> {
307 match data.dtype() {
308 DType::I64 => {
309 crate::ops::gather_scatter::scatter_nd::<i64>(data, indices, values, reduction)
310 }
311 DType::I32 => {
312 crate::ops::gather_scatter::scatter_nd::<i32>(data, indices, values, reduction)
313 }
314 DType::I16 => {
315 crate::ops::gather_scatter::scatter_nd::<i16>(data, indices, values, reduction)
316 }
317 DType::I8 => {
318 crate::ops::gather_scatter::scatter_nd::<i8>(data, indices, values, reduction)
319 }
320 DType::U64 => {
321 crate::ops::gather_scatter::scatter_nd::<u64>(data, indices, values, reduction)
322 }
323 DType::U32 => {
324 crate::ops::gather_scatter::scatter_nd::<u32>(data, indices, values, reduction)
325 }
326 DType::U16 => {
327 crate::ops::gather_scatter::scatter_nd::<u16>(data, indices, values, reduction)
328 }
329 DType::U8 => {
330 crate::ops::gather_scatter::scatter_nd::<u8>(data, indices, values, reduction)
331 }
332 dt => panic!("int_scatter_nd: unsupported dtype {:?}", dt),
333 }
334 }
335
336 fn int_gather_nd(data: IntTensor<Flex>, indices: IntTensor<Flex>) -> IntTensor<Flex> {
337 match data.dtype() {
338 DType::I64 => crate::ops::gather_scatter::gather_nd::<i64>(data, indices),
339 DType::I32 => crate::ops::gather_scatter::gather_nd::<i32>(data, indices),
340 DType::I16 => crate::ops::gather_scatter::gather_nd::<i16>(data, indices),
341 DType::I8 => crate::ops::gather_scatter::gather_nd::<i8>(data, indices),
342 DType::U64 => crate::ops::gather_scatter::gather_nd::<u64>(data, indices),
343 DType::U32 => crate::ops::gather_scatter::gather_nd::<u32>(data, indices),
344 DType::U16 => crate::ops::gather_scatter::gather_nd::<u16>(data, indices),
345 DType::U8 => crate::ops::gather_scatter::gather_nd::<u8>(data, indices),
346 dt => panic!("int_gather_nd: unsupported dtype {:?}", dt),
347 }
348 }
349
350 fn int_select(
355 tensor: IntTensor<Flex>,
356 dim: usize,
357 indices: IntTensor<Flex>,
358 ) -> IntTensor<Flex> {
359 match tensor.dtype() {
360 DType::I64 => crate::ops::gather_scatter::select::<i64>(tensor, dim, indices),
361 DType::I32 => crate::ops::gather_scatter::select::<i32>(tensor, dim, indices),
362 DType::I16 => crate::ops::gather_scatter::select::<i16>(tensor, dim, indices),
363 DType::I8 => crate::ops::gather_scatter::select::<i8>(tensor, dim, indices),
364 DType::U64 => crate::ops::gather_scatter::select::<u64>(tensor, dim, indices),
365 DType::U32 => crate::ops::gather_scatter::select::<u32>(tensor, dim, indices),
366 DType::U16 => crate::ops::gather_scatter::select::<u16>(tensor, dim, indices),
367 DType::U8 => crate::ops::gather_scatter::select::<u8>(tensor, dim, indices),
368 dt => panic!("int_select: unsupported dtype {:?}", dt),
369 }
370 }
371
372 fn int_select_assign(
373 tensor: IntTensor<Flex>,
374 dim: usize,
375 indices: IntTensor<Flex>,
376 value: IntTensor<Flex>,
377 update: burn_backend::tensor::IndexingUpdateOp,
378 ) -> IntTensor<Flex> {
379 match update {
380 burn_backend::tensor::IndexingUpdateOp::Assign => {
381 debug_assert_eq!(
382 tensor.dtype(),
383 value.dtype(),
384 "int_select_assign: dtype mismatch"
385 );
386 match tensor.dtype() {
387 DType::I64 => crate::ops::gather_scatter::select_assign::<i64>(
388 tensor, dim, indices, value,
389 ),
390 DType::I32 => crate::ops::gather_scatter::select_assign::<i32>(
391 tensor, dim, indices, value,
392 ),
393 DType::I16 => crate::ops::gather_scatter::select_assign::<i16>(
394 tensor, dim, indices, value,
395 ),
396 DType::I8 => {
397 crate::ops::gather_scatter::select_assign::<i8>(tensor, dim, indices, value)
398 }
399 DType::U64 => crate::ops::gather_scatter::select_assign::<u64>(
400 tensor, dim, indices, value,
401 ),
402 DType::U32 => crate::ops::gather_scatter::select_assign::<u32>(
403 tensor, dim, indices, value,
404 ),
405 DType::U16 => crate::ops::gather_scatter::select_assign::<u16>(
406 tensor, dim, indices, value,
407 ),
408 DType::U8 => {
409 crate::ops::gather_scatter::select_assign::<u8>(tensor, dim, indices, value)
410 }
411 dt => panic!("int_select_assign: unsupported dtype {:?}", dt),
412 }
413 }
414 burn_backend::tensor::IndexingUpdateOp::Add => {
415 debug_assert_eq!(
416 tensor.dtype(),
417 value.dtype(),
418 "int_select_assign: dtype mismatch"
419 );
420 match tensor.dtype() {
421 DType::I64 => {
422 crate::ops::gather_scatter::select_add::<i64>(tensor, dim, indices, value)
423 }
424 DType::I32 => {
425 crate::ops::gather_scatter::select_add::<i32>(tensor, dim, indices, value)
426 }
427 DType::I16 => {
428 crate::ops::gather_scatter::select_add::<i16>(tensor, dim, indices, value)
429 }
430 DType::I8 => {
431 crate::ops::gather_scatter::select_add::<i8>(tensor, dim, indices, value)
432 }
433 DType::U64 => {
434 crate::ops::gather_scatter::select_add::<u64>(tensor, dim, indices, value)
435 }
436 DType::U32 => {
437 crate::ops::gather_scatter::select_add::<u32>(tensor, dim, indices, value)
438 }
439 DType::U16 => {
440 crate::ops::gather_scatter::select_add::<u16>(tensor, dim, indices, value)
441 }
442 DType::U8 => {
443 crate::ops::gather_scatter::select_add::<u8>(tensor, dim, indices, value)
444 }
445 dt => panic!("int_select_assign: unsupported dtype {:?}", dt),
446 }
447 }
448 burn_backend::tensor::IndexingUpdateOp::Mul => {
449 debug_assert_eq!(
450 tensor.dtype(),
451 value.dtype(),
452 "int_select_assign: dtype mismatch"
453 );
454 match tensor.dtype() {
455 DType::I64 => {
456 crate::ops::gather_scatter::select_mul::<i64>(tensor, dim, indices, value)
457 }
458 DType::I32 => {
459 crate::ops::gather_scatter::select_mul::<i32>(tensor, dim, indices, value)
460 }
461 DType::I16 => {
462 crate::ops::gather_scatter::select_mul::<i16>(tensor, dim, indices, value)
463 }
464 DType::I8 => {
465 crate::ops::gather_scatter::select_mul::<i8>(tensor, dim, indices, value)
466 }
467 DType::U64 => {
468 crate::ops::gather_scatter::select_mul::<u64>(tensor, dim, indices, value)
469 }
470 DType::U32 => {
471 crate::ops::gather_scatter::select_mul::<u32>(tensor, dim, indices, value)
472 }
473 DType::U16 => {
474 crate::ops::gather_scatter::select_mul::<u16>(tensor, dim, indices, value)
475 }
476 DType::U8 => {
477 crate::ops::gather_scatter::select_mul::<u8>(tensor, dim, indices, value)
478 }
479 dt => panic!("int_select_assign: unsupported dtype {:?}", dt),
480 }
481 }
482 burn_backend::tensor::IndexingUpdateOp::Min => {
483 debug_assert_eq!(
484 tensor.dtype(),
485 value.dtype(),
486 "int_select_assign: dtype mismatch"
487 );
488 match tensor.dtype() {
489 DType::I64 => {
490 crate::ops::gather_scatter::select_min::<i64>(tensor, dim, indices, value)
491 }
492 DType::I32 => {
493 crate::ops::gather_scatter::select_min::<i32>(tensor, dim, indices, value)
494 }
495 DType::I16 => {
496 crate::ops::gather_scatter::select_min::<i16>(tensor, dim, indices, value)
497 }
498 DType::I8 => {
499 crate::ops::gather_scatter::select_min::<i8>(tensor, dim, indices, value)
500 }
501 DType::U64 => {
502 crate::ops::gather_scatter::select_min::<u64>(tensor, dim, indices, value)
503 }
504 DType::U32 => {
505 crate::ops::gather_scatter::select_min::<u32>(tensor, dim, indices, value)
506 }
507 DType::U16 => {
508 crate::ops::gather_scatter::select_min::<u16>(tensor, dim, indices, value)
509 }
510 DType::U8 => {
511 crate::ops::gather_scatter::select_min::<u8>(tensor, dim, indices, value)
512 }
513 dt => panic!("int_select_assign: unsupported dtype {:?}", dt),
514 }
515 }
516 burn_backend::tensor::IndexingUpdateOp::Max => {
517 debug_assert_eq!(
518 tensor.dtype(),
519 value.dtype(),
520 "int_select_assign: dtype mismatch"
521 );
522 match tensor.dtype() {
523 DType::I64 => {
524 crate::ops::gather_scatter::select_max::<i64>(tensor, dim, indices, value)
525 }
526 DType::I32 => {
527 crate::ops::gather_scatter::select_max::<i32>(tensor, dim, indices, value)
528 }
529 DType::I16 => {
530 crate::ops::gather_scatter::select_max::<i16>(tensor, dim, indices, value)
531 }
532 DType::I8 => {
533 crate::ops::gather_scatter::select_max::<i8>(tensor, dim, indices, value)
534 }
535 DType::U64 => {
536 crate::ops::gather_scatter::select_max::<u64>(tensor, dim, indices, value)
537 }
538 DType::U32 => {
539 crate::ops::gather_scatter::select_max::<u32>(tensor, dim, indices, value)
540 }
541 DType::U16 => {
542 crate::ops::gather_scatter::select_max::<u16>(tensor, dim, indices, value)
543 }
544 DType::U8 => {
545 crate::ops::gather_scatter::select_max::<u8>(tensor, dim, indices, value)
546 }
547 dt => panic!("int_select_assign: unsupported dtype {:?}", dt),
548 }
549 }
550 }
551 }
552
553 fn int_equal(
554 lhs: IntTensor<Flex>,
555 rhs: IntTensor<Flex>,
556 out_dtype: burn_std::BoolDType,
557 ) -> BoolTensor<Flex> {
558 crate::ops::comparison::int_equal(lhs, rhs, out_dtype)
559 }
560
561 fn int_equal_elem(
562 lhs: IntTensor<Flex>,
563 rhs: Scalar,
564 out_dtype: burn_std::BoolDType,
565 ) -> BoolTensor<Flex> {
566 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
567 crate::ops::comparison::int_equal_elem(lhs, i, u, out_dtype)
568 }
569
570 fn int_greater(
571 lhs: IntTensor<Flex>,
572 rhs: IntTensor<Flex>,
573 out_dtype: burn_std::BoolDType,
574 ) -> BoolTensor<Flex> {
575 crate::ops::comparison::int_greater(lhs, rhs, out_dtype)
576 }
577
578 fn int_greater_elem(
579 lhs: IntTensor<Flex>,
580 rhs: Scalar,
581 out_dtype: burn_std::BoolDType,
582 ) -> BoolTensor<Flex> {
583 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
584 crate::ops::comparison::int_greater_elem(lhs, i, u, out_dtype)
585 }
586
587 fn int_greater_equal(
588 lhs: IntTensor<Flex>,
589 rhs: IntTensor<Flex>,
590 out_dtype: burn_std::BoolDType,
591 ) -> BoolTensor<Flex> {
592 crate::ops::comparison::int_greater_equal(lhs, rhs, out_dtype)
593 }
594
595 fn int_greater_equal_elem(
596 lhs: IntTensor<Flex>,
597 rhs: Scalar,
598 out_dtype: burn_std::BoolDType,
599 ) -> BoolTensor<Flex> {
600 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
601 crate::ops::comparison::int_greater_equal_elem(lhs, i, u, out_dtype)
602 }
603
604 fn int_lower(
605 lhs: IntTensor<Flex>,
606 rhs: IntTensor<Flex>,
607 out_dtype: burn_std::BoolDType,
608 ) -> BoolTensor<Flex> {
609 crate::ops::comparison::int_lower(lhs, rhs, out_dtype)
610 }
611
612 fn int_lower_elem(
613 lhs: IntTensor<Flex>,
614 rhs: Scalar,
615 out_dtype: burn_std::BoolDType,
616 ) -> BoolTensor<Flex> {
617 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
618 crate::ops::comparison::int_lower_elem(lhs, i, u, out_dtype)
619 }
620
621 fn int_lower_equal(
622 lhs: IntTensor<Flex>,
623 rhs: IntTensor<Flex>,
624 out_dtype: burn_std::BoolDType,
625 ) -> BoolTensor<Flex> {
626 crate::ops::comparison::int_lower_equal(lhs, rhs, out_dtype)
627 }
628
629 fn int_lower_equal_elem(
630 lhs: IntTensor<Flex>,
631 rhs: Scalar,
632 out_dtype: burn_std::BoolDType,
633 ) -> BoolTensor<Flex> {
634 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
635 crate::ops::comparison::int_lower_equal_elem(lhs, i, u, out_dtype)
636 }
637
638 fn int_add(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
639 int_binary_op(lhs, rhs, |a, b| a + b)
640 }
641
642 fn int_add_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
643 if lhs.dtype() == DType::U64 {
644 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| {
645 a.wrapping_add(b)
646 });
647 }
648 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a + b)
649 }
650
651 fn int_sub(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
652 int_binary_op(lhs, rhs, |a, b| a - b)
653 }
654
655 fn int_sub_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
656 if lhs.dtype() == DType::U64 {
657 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| {
658 a.wrapping_sub(b)
659 });
660 }
661 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a - b)
662 }
663
664 fn int_mul(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
665 int_binary_op(lhs, rhs, |a, b| a * b)
666 }
667
668 fn int_mul_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
669 if lhs.dtype() == DType::U64 {
670 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| {
671 a.wrapping_mul(b)
672 });
673 }
674 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a * b)
675 }
676
677 fn int_div(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
678 if lhs.dtype() == DType::U64 {
680 let (lhs, rhs) = crate::ops::expand::broadcast_binary(lhs, rhs);
681 return binary_op_typed(lhs, rhs, |a: u64, b: u64| a / b);
682 }
683 int_binary_op(lhs, rhs, |a, b| a / b)
684 }
685
686 fn int_div_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
687 if lhs.dtype() == DType::U64 {
688 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a / b);
689 }
690 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a / b)
691 }
692
693 fn int_remainder(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
694 if lhs.dtype() == DType::U64 {
696 let (lhs, rhs) = crate::ops::expand::broadcast_binary(lhs, rhs);
697 return binary_op_typed(lhs, rhs, |a: u64, b: u64| a % b);
698 }
699 int_binary_op(lhs, rhs, remainder_int)
701 }
702
703 fn int_remainder_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
704 if lhs.dtype() == DType::U64 {
705 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a % b);
706 }
707 int_scalar_op(lhs, rhs.to_i64().unwrap(), remainder_int)
709 }
710
711 fn int_into_float(
713 tensor: IntTensor<Flex>,
714 out_dtype: burn_std::FloatDType,
715 ) -> FloatTensor<Flex> {
716 let tensor = tensor.to_contiguous();
717 let shape = tensor.layout().shape().clone();
718 let src = tensor.dtype();
719 let out_dt = DType::from(out_dtype);
720
721 macro_rules! read_ints {
724 (|$x:ident| $conv:expr) => {
725 match src {
726 DType::I64 => tensor.storage::<i64>().iter().map(|&$x| $conv).collect(),
727 DType::I32 => tensor.storage::<i32>().iter().map(|&$x| $conv).collect(),
728 DType::I16 => tensor.storage::<i16>().iter().map(|&$x| $conv).collect(),
729 DType::I8 => tensor.storage::<i8>().iter().map(|&$x| $conv).collect(),
730 DType::U64 => tensor.storage::<u64>().iter().map(|&$x| $conv).collect(),
731 DType::U32 => tensor.storage::<u32>().iter().map(|&$x| $conv).collect(),
732 DType::U16 => tensor.storage::<u16>().iter().map(|&$x| $conv).collect(),
733 DType::U8 => tensor.storage::<u8>().iter().map(|&$x| $conv).collect(),
734 _ => panic!("int_into_float: unsupported source dtype {:?}", src),
735 }
736 };
737 }
738
739 match out_dtype {
740 FloatDType::F64 => {
741 let data: Vec<f64> = read_ints!(|x| x as f64);
742 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
743 }
744 FloatDType::F32 | FloatDType::Flex32 => {
745 let data: Vec<f32> = read_ints!(|x| x as f32);
746 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
747 }
748 FloatDType::F16 => {
749 let data: Vec<f16> = read_ints!(|x| f16::from_f32(x as f32));
750 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
751 }
752 FloatDType::BF16 => {
753 let data: Vec<bf16> = read_ints!(|x| bf16::from_f32(x as f32));
754 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
755 }
756 }
757 }
758
759 fn int_swap_dims(tensor: IntTensor<Flex>, dim1: usize, dim2: usize) -> IntTensor<Flex> {
760 tensor.transpose(dim1, dim2)
761 }
762
763 fn int_permute(tensor: IntTensor<Flex>, axes: &[usize]) -> IntTensor<Flex> {
764 tensor.permute(axes)
765 }
766
767 fn int_flip(tensor: IntTensor<Flex>, axes: &[usize]) -> IntTensor<Flex> {
768 crate::ops::flip::flip(tensor, axes)
769 }
770
771 fn int_random(
772 shape: Shape,
773 distribution: Distribution,
774 _device: &Device<Flex>,
775 dtype: IntDType,
776 ) -> IntTensor<Flex> {
777 let mut seed = crate::backend::SEED.lock();
778 let mut rng = seed.take().unwrap_or_else(crate::backend::get_seeded_rng);
779 let data = match dtype {
780 IntDType::I64 => TensorData::random::<i64, _, _>(shape, distribution, &mut rng),
781 IntDType::I32 => TensorData::random::<i32, _, _>(shape, distribution, &mut rng),
782 IntDType::I16 => TensorData::random::<i16, _, _>(shape, distribution, &mut rng),
783 IntDType::I8 => TensorData::random::<i8, _, _>(shape, distribution, &mut rng),
784 IntDType::U64 => TensorData::random::<u64, _, _>(shape, distribution, &mut rng),
785 IntDType::U32 => TensorData::random::<u32, _, _>(shape, distribution, &mut rng),
786 IntDType::U16 => TensorData::random::<u16, _, _>(shape, distribution, &mut rng),
787 IntDType::U8 => TensorData::random::<u8, _, _>(shape, distribution, &mut rng),
788 };
789 *seed = Some(rng);
790 FlexTensor::from_data(data)
791 }
792
793 fn int_expand(tensor: IntTensor<Flex>, shape: Shape) -> IntTensor<Flex> {
794 crate::ops::expand::expand(tensor, shape)
795 }
796
797 fn int_matmul(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
798 matmul::int_matmul(lhs, rhs)
799 }
800
801 fn int_sum(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
802 crate::ops::reduce::sum(tensor)
803 }
804
805 fn int_sum_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
806 crate::ops::reduce::sum_dim(tensor, dim)
807 }
808
809 fn int_prod(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
810 crate::ops::reduce::prod(tensor)
811 }
812
813 fn int_prod_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
814 crate::ops::reduce::prod_dim(tensor, dim)
815 }
816
817 fn int_mean_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
818 crate::ops::reduce::mean_dim(tensor, dim)
819 }
820
821 fn int_cumsum(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
822 match tensor.dtype() {
823 DType::I64 => crate::ops::cumulative::cumsum::<i64>(tensor, dim),
824 DType::I32 => crate::ops::cumulative::cumsum::<i32>(tensor, dim),
825 DType::I16 => crate::ops::cumulative::cumsum::<i16>(tensor, dim),
826 DType::I8 => crate::ops::cumulative::cumsum::<i8>(tensor, dim),
827 DType::U64 => crate::ops::cumulative::cumsum::<u64>(tensor, dim),
828 DType::U32 => crate::ops::cumulative::cumsum::<u32>(tensor, dim),
829 DType::U16 => crate::ops::cumulative::cumsum::<u16>(tensor, dim),
830 DType::U8 => crate::ops::cumulative::cumsum::<u8>(tensor, dim),
831 dt => panic!("int_cumsum: unsupported dtype {:?}", dt),
832 }
833 }
834
835 fn int_cumprod(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
836 match tensor.dtype() {
837 DType::I64 => crate::ops::cumulative::cumprod::<i64>(tensor, dim),
838 DType::I32 => crate::ops::cumulative::cumprod::<i32>(tensor, dim),
839 DType::I16 => crate::ops::cumulative::cumprod::<i16>(tensor, dim),
840 DType::I8 => crate::ops::cumulative::cumprod::<i8>(tensor, dim),
841 DType::U64 => crate::ops::cumulative::cumprod::<u64>(tensor, dim),
842 DType::U32 => crate::ops::cumulative::cumprod::<u32>(tensor, dim),
843 DType::U16 => crate::ops::cumulative::cumprod::<u16>(tensor, dim),
844 DType::U8 => crate::ops::cumulative::cumprod::<u8>(tensor, dim),
845 dt => panic!("int_cumprod: unsupported dtype {:?}", dt),
846 }
847 }
848
849 fn int_cummin(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
850 match tensor.dtype() {
851 DType::I64 => crate::ops::cumulative::cummin::<i64>(tensor, dim),
852 DType::I32 => crate::ops::cumulative::cummin::<i32>(tensor, dim),
853 DType::I16 => crate::ops::cumulative::cummin::<i16>(tensor, dim),
854 DType::I8 => crate::ops::cumulative::cummin::<i8>(tensor, dim),
855 DType::U64 => crate::ops::cumulative::cummin::<u64>(tensor, dim),
856 DType::U32 => crate::ops::cumulative::cummin::<u32>(tensor, dim),
857 DType::U16 => crate::ops::cumulative::cummin::<u16>(tensor, dim),
858 DType::U8 => crate::ops::cumulative::cummin::<u8>(tensor, dim),
859 dt => panic!("int_cummin: unsupported dtype {:?}", dt),
860 }
861 }
862
863 fn int_cummax(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
864 match tensor.dtype() {
865 DType::I64 => crate::ops::cumulative::cummax::<i64>(tensor, dim),
866 DType::I32 => crate::ops::cumulative::cummax::<i32>(tensor, dim),
867 DType::I16 => crate::ops::cumulative::cummax::<i16>(tensor, dim),
868 DType::I8 => crate::ops::cumulative::cummax::<i8>(tensor, dim),
869 DType::U64 => crate::ops::cumulative::cummax::<u64>(tensor, dim),
870 DType::U32 => crate::ops::cumulative::cummax::<u32>(tensor, dim),
871 DType::U16 => crate::ops::cumulative::cummax::<u16>(tensor, dim),
872 DType::U8 => crate::ops::cumulative::cummax::<u8>(tensor, dim),
873 dt => panic!("int_cummax: unsupported dtype {:?}", dt),
874 }
875 }
876
877 fn int_argmax(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
878 crate::ops::reduce::argmax(tensor, dim)
879 }
880
881 fn int_argmin(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
882 crate::ops::reduce::argmin(tensor, dim)
883 }
884
885 fn int_abs(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
886 crate::ops::unary::int_abs(tensor)
887 }
888
889 fn bitwise_and(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
890 int_binary_op(lhs, rhs, |a, b| a & b)
891 }
892
893 fn bitwise_and_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
894 if lhs.dtype() == DType::U64 {
895 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a & b);
896 }
897 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a & b)
898 }
899
900 fn bitwise_or(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
901 int_binary_op(lhs, rhs, |a, b| a | b)
902 }
903
904 fn bitwise_or_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
905 if lhs.dtype() == DType::U64 {
906 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a | b);
907 }
908 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a | b)
909 }
910
911 fn bitwise_xor(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
912 int_binary_op(lhs, rhs, |a, b| a ^ b)
913 }
914
915 fn bitwise_xor_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
916 if lhs.dtype() == DType::U64 {
917 return scalar_op_typed(lhs, rhs.to_u64().unwrap(), |a: u64, b: u64| a ^ b);
918 }
919 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a ^ b)
920 }
921
922 fn bitwise_not(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
923 int_scalar_op(tensor, 0, |a, _| !a)
925 }
926
927 fn bitwise_left_shift(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
931 int_binary_op(lhs, rhs, |a, b| a.wrapping_shl(b as u32))
932 }
933
934 fn bitwise_left_shift_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
935 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a.wrapping_shl(b as u32))
936 }
937
938 fn bitwise_right_shift(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
941 if lhs.dtype() == DType::U64 {
942 let (lhs, rhs) = crate::ops::expand::broadcast_binary(lhs, rhs);
943 return binary_op_typed(lhs, rhs, |a: u64, b: u64| a.wrapping_shr(b as u32));
944 }
945 int_binary_op(lhs, rhs, |a, b| a.wrapping_shr(b as u32))
946 }
947
948 fn bitwise_right_shift_scalar(lhs: IntTensor<Flex>, rhs: Scalar) -> IntTensor<Flex> {
949 if lhs.dtype() == DType::U64 {
950 return scalar_op_typed(lhs, rhs.to_i64().unwrap() as u64, |a: u64, b: u64| {
951 a.wrapping_shr(b as u32)
952 });
953 }
954 int_scalar_op(lhs, rhs.to_i64().unwrap(), |a, b| a.wrapping_shr(b as u32))
955 }
956
957 fn int_cast(tensor: IntTensor<Flex>, dtype: IntDType) -> IntTensor<Flex> {
958 let target_dtype: DType = dtype.into();
959
960 if tensor.dtype() == target_dtype {
962 return tensor;
963 }
964
965 let tensor = tensor.to_contiguous();
967 let shape = tensor.layout().shape().clone();
968
969 macro_rules! cast_impl {
971 ($storage:ident, $dst_type:ty) => {{
972 Some(Bytes::from_elems(
973 $storage
974 .iter()
975 .map(|&x| x as $dst_type)
976 .collect::<Vec<$dst_type>>(),
977 ))
978 }};
979 }
980
981 let bytes = match tensor.dtype() {
983 DType::I64 => {
985 let storage: &[i64] = tensor.storage();
986 match target_dtype {
987 DType::I32 => cast_impl!(storage, i32),
988 DType::I16 => cast_impl!(storage, i16),
989 DType::I8 => cast_impl!(storage, i8),
990 DType::U64 => cast_impl!(storage, u64),
991 DType::U32 => cast_impl!(storage, u32),
992 DType::U16 => cast_impl!(storage, u16),
993 DType::U8 => cast_impl!(storage, u8),
994 _ => None,
995 }
996 }
997
998 DType::I32 => {
1000 let storage: &[i32] = tensor.storage();
1001 match target_dtype {
1002 DType::I64 => cast_impl!(storage, i64),
1003 DType::I16 => cast_impl!(storage, i16),
1004 DType::I8 => cast_impl!(storage, i8),
1005 DType::U64 => cast_impl!(storage, u64),
1006 DType::U32 => cast_impl!(storage, u32),
1007 DType::U16 => cast_impl!(storage, u16),
1008 DType::U8 => cast_impl!(storage, u8),
1009 _ => None,
1010 }
1011 }
1012
1013 DType::I16 => {
1015 let storage: &[i16] = tensor.storage();
1016 match target_dtype {
1017 DType::I64 => cast_impl!(storage, i64),
1018 DType::I32 => cast_impl!(storage, i32),
1019 DType::I8 => cast_impl!(storage, i8),
1020 DType::U64 => cast_impl!(storage, u64),
1021 DType::U32 => cast_impl!(storage, u32),
1022 DType::U16 => cast_impl!(storage, u16),
1023 DType::U8 => cast_impl!(storage, u8),
1024 _ => None,
1025 }
1026 }
1027
1028 DType::I8 => {
1030 let storage: &[i8] = tensor.storage();
1031 match target_dtype {
1032 DType::I64 => cast_impl!(storage, i64),
1033 DType::I32 => cast_impl!(storage, i32),
1034 DType::I16 => cast_impl!(storage, i16),
1035 DType::U64 => cast_impl!(storage, u64),
1036 DType::U32 => cast_impl!(storage, u32),
1037 DType::U16 => cast_impl!(storage, u16),
1038 DType::U8 => cast_impl!(storage, u8),
1039 _ => None,
1040 }
1041 }
1042
1043 DType::U64 => {
1045 let storage: &[u64] = tensor.storage();
1046 match target_dtype {
1047 DType::I64 => cast_impl!(storage, i64),
1048 DType::I32 => cast_impl!(storage, i32),
1049 DType::I16 => cast_impl!(storage, i16),
1050 DType::I8 => cast_impl!(storage, i8),
1051 DType::U32 => cast_impl!(storage, u32),
1052 DType::U16 => cast_impl!(storage, u16),
1053 DType::U8 => cast_impl!(storage, u8),
1054 _ => None,
1055 }
1056 }
1057
1058 DType::U32 => {
1060 let storage: &[u32] = tensor.storage();
1061 match target_dtype {
1062 DType::I64 => cast_impl!(storage, i64),
1063 DType::I32 => cast_impl!(storage, i32),
1064 DType::I16 => cast_impl!(storage, i16),
1065 DType::I8 => cast_impl!(storage, i8),
1066 DType::U64 => cast_impl!(storage, u64),
1067 DType::U16 => cast_impl!(storage, u16),
1068 DType::U8 => cast_impl!(storage, u8),
1069 _ => None,
1070 }
1071 }
1072
1073 DType::U16 => {
1075 let storage: &[u16] = tensor.storage();
1076 match target_dtype {
1077 DType::I64 => cast_impl!(storage, i64),
1078 DType::I32 => cast_impl!(storage, i32),
1079 DType::I16 => cast_impl!(storage, i16),
1080 DType::I8 => cast_impl!(storage, i8),
1081 DType::U64 => cast_impl!(storage, u64),
1082 DType::U32 => cast_impl!(storage, u32),
1083 DType::U8 => cast_impl!(storage, u8),
1084 _ => None,
1085 }
1086 }
1087
1088 DType::U8 => {
1090 let storage: &[u8] = tensor.storage();
1091 match target_dtype {
1092 DType::I64 => cast_impl!(storage, i64),
1093 DType::I32 => cast_impl!(storage, i32),
1094 DType::I16 => cast_impl!(storage, i16),
1095 DType::I8 => cast_impl!(storage, i8),
1096 DType::U64 => cast_impl!(storage, u64),
1097 DType::U32 => cast_impl!(storage, u32),
1098 DType::U16 => cast_impl!(storage, u16),
1099 _ => None,
1100 }
1101 }
1102
1103 _ => None,
1104 };
1105 let Some(bytes) = bytes else {
1106 panic!(
1107 "int_cast: unsupported conversion from {:?} to {:?}",
1108 tensor.dtype(),
1109 target_dtype
1110 )
1111 };
1112 FlexTensor::new(bytes, Layout::contiguous(shape), target_dtype)
1113 }
1114
1115 fn int_unfold(
1116 tensor: IntTensor<Flex>,
1117 dim: usize,
1118 size: usize,
1119 step: usize,
1120 ) -> IntTensor<Flex> {
1121 crate::ops::unfold::unfold_int(tensor, dim, size, step)
1122 }
1123
1124 fn int_neg(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
1125 int_scalar_op(tensor, 0i64, |a, _| a.wrapping_neg())
1126 }
1127
1128 fn int_clamp(tensor: IntTensor<Flex>, min: Scalar, max: Scalar) -> IntTensor<Flex> {
1129 if tensor.dtype() == DType::U64 {
1130 let min_val = min.to_u64().unwrap();
1131 let max_val = max.to_u64().unwrap();
1132 return scalar_op_typed(tensor, 0u64, move |x: u64, _| x.clamp(min_val, max_val));
1133 }
1134 let min_val = min.to_i64().unwrap();
1135 let max_val = max.to_i64().unwrap();
1136 int_scalar_op(tensor, 0i64, move |x, _| x.clamp(min_val, max_val))
1137 }
1138
1139 fn int_clamp_min(tensor: IntTensor<Flex>, min: Scalar) -> IntTensor<Flex> {
1140 if tensor.dtype() == DType::U64 {
1141 let min_val = min.to_u64().unwrap();
1142 return scalar_op_typed(tensor, 0u64, move |x: u64, _| x.max(min_val));
1143 }
1144 let min_val = min.to_i64().unwrap();
1145 int_scalar_op(tensor, 0i64, move |x, _| x.max(min_val))
1146 }
1147
1148 fn int_clamp_max(tensor: IntTensor<Flex>, max: Scalar) -> IntTensor<Flex> {
1149 if tensor.dtype() == DType::U64 {
1150 let max_val = max.to_u64().unwrap();
1151 return scalar_op_typed(tensor, 0u64, move |x: u64, _| x.min(max_val));
1152 }
1153 let max_val = max.to_i64().unwrap();
1154 int_scalar_op(tensor, 0i64, move |x, _| x.min(max_val))
1155 }
1156
1157 fn int_sign(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
1158 if tensor.dtype() == DType::U64 {
1159 return scalar_op_typed(tensor, 0u64, |x: u64, _| if x > 0 { 1 } else { 0 });
1160 }
1161 int_scalar_op(tensor, 0i64, |x, _| {
1162 if x > 0 {
1163 1
1164 } else if x < 0 {
1165 -1
1166 } else {
1167 0
1168 }
1169 })
1170 }
1171
1172 fn int_mean(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
1173 let n = tensor.layout().num_elements();
1174 assert!(n > 0, "int_mean: cannot take mean of empty tensor");
1175 let dtype = tensor.dtype();
1176 let sum_result = crate::ops::reduce::sum(tensor);
1177 macro_rules! compute_mean {
1179 ($ty:ty) => {{
1180 let data: &[$ty] = sum_result.storage();
1181 let mean_val = (data[0] as i64 / n as i64) as $ty;
1182 FlexTensor::new(
1183 Bytes::from_elems(alloc::vec![mean_val]),
1184 Layout::contiguous(Shape::from(alloc::vec![1])),
1185 dtype,
1186 )
1187 }};
1188 }
1189 match dtype {
1190 DType::I64 => compute_mean!(i64),
1191 DType::I32 => compute_mean!(i32),
1192 DType::I16 => compute_mean!(i16),
1193 DType::I8 => compute_mean!(i8),
1194 other => panic!("int_mean: unsupported dtype {:?}", other),
1195 }
1196 }
1197
1198 fn int_max(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
1199 crate::ops::reduce::max(tensor)
1200 }
1201
1202 fn int_max_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
1203 crate::ops::reduce::max_dim(tensor, dim)
1204 }
1205
1206 fn int_min(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
1207 crate::ops::reduce::min(tensor)
1208 }
1209
1210 fn int_min_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
1211 crate::ops::reduce::min_dim(tensor, dim)
1212 }
1213
1214 fn int_max_dim_with_indices(
1215 tensor: IntTensor<Flex>,
1216 dim: usize,
1217 ) -> (IntTensor<Flex>, IntTensor<Flex>) {
1218 crate::ops::reduce::max_dim_with_indices(tensor, dim)
1219 }
1220
1221 fn int_min_dim_with_indices(
1222 tensor: IntTensor<Flex>,
1223 dim: usize,
1224 ) -> (IntTensor<Flex>, IntTensor<Flex>) {
1225 crate::ops::reduce::min_dim_with_indices(tensor, dim)
1226 }
1227
1228 fn int_any(tensor: IntTensor<Flex>, out_dtype: burn_std::BoolDType) -> BoolTensor<Flex> {
1229 crate::ops::comparison::any_int(tensor, out_dtype)
1230 }
1231
1232 fn int_any_dim(
1233 tensor: IntTensor<Flex>,
1234 dim: usize,
1235 out_dtype: burn_std::BoolDType,
1236 ) -> BoolTensor<Flex> {
1237 crate::ops::comparison::any_int_dim(tensor, dim, out_dtype)
1238 }
1239
1240 fn int_all(tensor: IntTensor<Flex>, out_dtype: burn_std::BoolDType) -> BoolTensor<Flex> {
1241 crate::ops::comparison::all_int(tensor, out_dtype)
1242 }
1243
1244 fn int_all_dim(
1245 tensor: IntTensor<Flex>,
1246 dim: usize,
1247 out_dtype: burn_std::BoolDType,
1248 ) -> BoolTensor<Flex> {
1249 crate::ops::comparison::all_int_dim(tensor, dim, out_dtype)
1250 }
1251
1252 fn int_powi(lhs: IntTensor<Flex>, rhs: IntTensor<Flex>) -> IntTensor<Flex> {
1253 int_binary_op(lhs, rhs, |a, b| a.wrapping_pow(b as u32))
1254 }
1255
1256 fn int_zeros(shape: Shape, _device: &Device<Flex>, dtype: IntDType) -> IntTensor<Flex> {
1257 FlexTensor::zeros(shape, dtype.into())
1258 }
1259
1260 fn int_ones(shape: Shape, _device: &Device<Flex>, dtype: IntDType) -> IntTensor<Flex> {
1261 let dt: DType = dtype.into();
1262 match dt {
1263 DType::I64 => FlexTensor::filled_typed(shape, dt, 1i64),
1264 DType::I32 => FlexTensor::filled_typed(shape, dt, 1i32),
1265 DType::I16 => FlexTensor::filled_typed(shape, dt, 1i16),
1266 DType::I8 => FlexTensor::filled_typed(shape, dt, 1i8),
1267 DType::U64 => FlexTensor::filled_typed(shape, dt, 1u64),
1268 DType::U32 => FlexTensor::filled_typed(shape, dt, 1u32),
1269 DType::U16 => FlexTensor::filled_typed(shape, dt, 1u16),
1270 DType::U8 => FlexTensor::filled_typed(shape, dt, 1u8),
1271 _ => unreachable!(),
1272 }
1273 }
1274
1275 fn int_full(
1276 shape: Shape,
1277 fill_value: burn_backend::Scalar,
1278 _device: &Device<Flex>,
1279 dtype: IntDType,
1280 ) -> IntTensor<Flex> {
1281 let dt: DType = dtype.into();
1282 let v = fill_value.to_i64().unwrap();
1283 match dt {
1284 DType::I64 => FlexTensor::filled_typed(shape, dt, v),
1285 DType::I32 => FlexTensor::filled_typed(shape, dt, v as i32),
1286 DType::I16 => FlexTensor::filled_typed(shape, dt, v as i16),
1287 DType::I8 => FlexTensor::filled_typed(shape, dt, v as i8),
1288 DType::U64 => FlexTensor::filled_typed(shape, dt, v as u64),
1289 DType::U32 => FlexTensor::filled_typed(shape, dt, v as u32),
1290 DType::U16 => FlexTensor::filled_typed(shape, dt, v as u16),
1291 DType::U8 => FlexTensor::filled_typed(shape, dt, v as u8),
1292 _ => unreachable!(),
1293 }
1294 }
1295
1296 fn int_transpose(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
1297 let ndims = tensor.layout().num_dims();
1298 if ndims < 2 {
1299 return tensor;
1300 }
1301 tensor.transpose(ndims - 2, ndims - 1)
1302 }
1303
1304 fn int_repeat_dim(tensor: IntTensor<Flex>, dim: usize, times: usize) -> IntTensor<Flex> {
1305 crate::ops::repeat_dim::repeat_dim(tensor, dim, times)
1306 }
1307
1308 fn int_not_equal(
1309 lhs: IntTensor<Flex>,
1310 rhs: IntTensor<Flex>,
1311 out_dtype: burn_std::BoolDType,
1312 ) -> BoolTensor<Flex> {
1313 crate::ops::comparison::int_not_equal(lhs, rhs, out_dtype)
1314 }
1315
1316 fn int_not_equal_elem(
1317 lhs: IntTensor<Flex>,
1318 rhs: burn_backend::Scalar,
1319 out_dtype: burn_std::BoolDType,
1320 ) -> BoolTensor<Flex> {
1321 let (i, u) = scalar_to_int_pair(lhs.dtype(), &rhs);
1322 crate::ops::comparison::int_not_equal_elem(lhs, i, u, out_dtype)
1323 }
1324
1325 fn int_sort(tensor: IntTensor<Flex>, dim: usize, descending: bool) -> IntTensor<Flex> {
1326 crate::ops::sort::sort(tensor, dim, descending)
1327 }
1328
1329 fn int_sort_with_indices(
1330 tensor: IntTensor<Flex>,
1331 dim: usize,
1332 descending: bool,
1333 ) -> (IntTensor<Flex>, IntTensor<Flex>) {
1334 crate::ops::sort::sort_with_indices(tensor, dim, descending)
1335 }
1336
1337 fn int_argsort(tensor: IntTensor<Flex>, dim: usize, descending: bool) -> IntTensor<Flex> {
1338 crate::ops::sort::argsort(tensor, dim, descending)
1339 }
1340
1341 fn int_powi_scalar(lhs: IntTensor<Flex>, rhs: burn_backend::Scalar) -> IntTensor<Flex> {
1342 use num_traits::ToPrimitive;
1343 match rhs.to_i64().unwrap() {
1344 0 => Self::int_ones(lhs.shape(), &Default::default(), lhs.dtype().into()),
1345 1 => lhs,
1346 2 => Self::int_mul(lhs.clone(), lhs),
1347 _ => Self::int_powi_scalar_impl(lhs, rhs),
1348 }
1349 }
1350
1351 fn int_powi_scalar_impl(lhs: IntTensor<Flex>, rhs: burn_backend::Scalar) -> IntTensor<Flex> {
1352 use num_traits::ToPrimitive;
1353 let exp = rhs.to_i64().unwrap() as u32;
1354 if lhs.dtype() == DType::U64 {
1355 return scalar_op_typed(lhs, exp as u64, move |x: u64, _| x.wrapping_pow(exp));
1356 }
1357 int_scalar_op(lhs, exp as i64, move |x, _| x.wrapping_pow(exp))
1358 }
1359
1360 fn int_max_abs(tensor: IntTensor<Flex>) -> IntTensor<Flex> {
1361 let abs = Self::int_abs(tensor);
1362 crate::ops::reduce::max(abs)
1363 }
1364
1365 fn int_max_abs_dim(tensor: IntTensor<Flex>, dim: usize) -> IntTensor<Flex> {
1366 let abs = Self::int_abs(tensor);
1367 crate::ops::reduce::max_dim(abs, dim)
1368 }
1369
1370 fn int_arange(
1371 range: core::ops::Range<i64>,
1372 _device: &Device<Flex>,
1373 dtype: IntDType,
1374 ) -> IntTensor<Flex> {
1375 Self::int_arange_step(range, 1, &Default::default(), dtype)
1376 }
1377
1378 fn int_arange_step(
1379 range: core::ops::Range<i64>,
1380 step: usize,
1381 _device: &Device<Flex>,
1382 dtype: IntDType,
1383 ) -> IntTensor<Flex> {
1384 let dt: DType = dtype.into();
1385
1386 macro_rules! arange_typed {
1387 ($ty:ty) => {{
1388 let data: Vec<$ty> = range.step_by(step).map(|v| v as $ty).collect();
1389 let shape = Shape::from(alloc::vec![data.len()]);
1390 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), dt)
1391 }};
1392 }
1393
1394 match dt {
1395 DType::I64 => arange_typed!(i64),
1396 DType::I32 => arange_typed!(i32),
1397 DType::I16 => arange_typed!(i16),
1398 DType::I8 => arange_typed!(i8),
1399 DType::U64 => arange_typed!(u64),
1400 DType::U32 => arange_typed!(u32),
1401 DType::U16 => arange_typed!(u16),
1402 DType::U8 => arange_typed!(u8),
1403 _ => unreachable!(),
1404 }
1405 }
1406}
1407
1408#[cfg(test)]
1418mod tests {
1419 use alloc::vec;
1420 use burn_backend::TensorData;
1421 use burn_backend::ops::IntTensorOps;
1422
1423 use crate::Flex;
1424 use crate::FlexTensor;
1425
1426 #[test]
1427 fn test_i64_remainder_overflow() {
1428 for (a, b, expected) in [(i64::MAX - 1, i64::MAX, i64::MAX - 1), (i64::MIN, -1, 0)] {
1431 let lhs = FlexTensor::from_data(TensorData::new(vec![a], [1]));
1432 let rhs = FlexTensor::from_data(TensorData::new(vec![b], [1]));
1433 for result in [
1434 Flex::int_remainder(lhs.clone(), rhs),
1435 Flex::int_remainder_scalar(lhs, b.into()),
1436 ] {
1437 let values: Vec<i64> = result.into_data().try_into_vec().unwrap();
1438 assert_eq!(values, vec![expected]);
1439 }
1440 }
1441 }
1442
1443 #[test]
1444 fn test_u64_div_large_values() {
1445 let a = FlexTensor::from_data(TensorData::new(vec![u64::MAX], [1]));
1446 let b = FlexTensor::from_data(TensorData::new(vec![2u64], [1]));
1447 let result = Flex::int_div(a, b);
1448 let values: Vec<u64> = bytemuck::cast_slice(&result.into_data().bytes).to_vec();
1449 assert_eq!(values[0], u64::MAX / 2);
1450 }
1451
1452 #[test]
1453 fn test_u64_remainder_large_values() {
1454 let a = FlexTensor::from_data(TensorData::new(vec![u64::MAX], [1]));
1455 let b = FlexTensor::from_data(TensorData::new(vec![2u64], [1]));
1456 let result = Flex::int_remainder(a, b);
1457 let values: Vec<u64> = bytemuck::cast_slice(&result.into_data().bytes).to_vec();
1458 assert_eq!(values[0], u64::MAX % 2);
1459 }
1460
1461 #[test]
1462 fn test_int_abs_min_value() {
1463 let a = FlexTensor::from_data(TensorData::new(vec![i64::MIN], [1]));
1465 let result = Flex::int_abs(a);
1466 let values: Vec<i64> = bytemuck::cast_slice(&result.into_data().bytes).to_vec();
1467 assert_eq!(values[0], i64::MIN.wrapping_abs());
1468 }
1469
1470 #[test]
1471 fn test_int_neg_min_value() {
1472 let a = FlexTensor::from_data(TensorData::new(vec![i64::MIN], [1]));
1474 let result = Flex::int_neg(a);
1475 let values: Vec<i64> = bytemuck::cast_slice(&result.into_data().bytes).to_vec();
1476 assert_eq!(values[0], i64::MIN.wrapping_neg());
1477 }
1478
1479 #[test]
1480 fn test_int_shift_large_amount() {
1481 let a = FlexTensor::from_data(TensorData::new(vec![1i64], [1]));
1483 let b = FlexTensor::from_data(TensorData::new(vec![64i64], [1]));
1484 let _left = Flex::bitwise_left_shift(a.clone(), b.clone());
1485 let _right = Flex::bitwise_right_shift(a, b);
1486 }
1487
1488 #[test]
1489 fn test_int_shift_masks_to_64_not_operand_width() {
1490 let a = FlexTensor::from_data(TensorData::new(vec![1i32], [1]));
1494 let b = FlexTensor::from_data(TensorData::new(vec![33i32], [1]));
1495 let result = Flex::bitwise_left_shift(a, b);
1496 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1497 assert_eq!(data, vec![0i32]);
1498 assert_eq!(1i32.wrapping_shl(33), 2i32);
1499 }
1500
1501 #[test]
1502 fn test_u64_right_shift_is_logical() {
1503 let values = vec![u64::MAX, 1u64 << 63, 12];
1505 let a = FlexTensor::from_data(TensorData::new(values.clone(), [3]));
1506 let b = FlexTensor::from_data(TensorData::new(vec![1u64, 63, 2], [3]));
1507 let result = Flex::bitwise_right_shift(a.clone(), b);
1508 let data: Vec<u64> = result.into_data().try_into_vec().unwrap();
1509 assert_eq!(data, vec![u64::MAX >> 1, 1, 3]);
1510
1511 let result = Flex::bitwise_right_shift_scalar(a, burn_backend::Scalar::from(1i64));
1512 let data: Vec<u64> = result.into_data().try_into_vec().unwrap();
1513 assert_eq!(data, values.iter().map(|v| v >> 1).collect::<Vec<_>>());
1514 }
1515
1516 #[test]
1517 fn test_u64_right_shift_broadcast() {
1518 let a = FlexTensor::from_data(TensorData::new(vec![u64::MAX, 1u64 << 63], [2, 1]));
1519 let b = FlexTensor::from_data(TensorData::new(vec![1u64, 4], [1, 2]));
1520 let result = Flex::bitwise_right_shift(a, b);
1521 assert_eq!(result.layout().shape().to_vec(), vec![2, 2]);
1522 let data: Vec<u64> = result.into_data().try_into_vec().unwrap();
1523 assert_eq!(
1524 data,
1525 vec![u64::MAX >> 1, u64::MAX >> 4, 1u64 << 62, 1u64 << 59]
1526 );
1527 }
1528
1529 #[test]
1530 fn test_int_into_float_f64() {
1531 use burn_backend::ops::IntTensorOps;
1532 use burn_std::FloatDType;
1533
1534 let t = FlexTensor::from_data(TensorData::new(vec![1i64, 2, -3], [3]));
1535 let result = Flex::int_into_float(t, FloatDType::F64);
1536 assert_eq!(result.dtype(), burn_backend::DType::F64);
1537 let data: Vec<f64> = result.into_data().try_into_vec().unwrap();
1538 assert_eq!(data, vec![1.0f64, 2.0, -3.0]);
1539 }
1540
1541 #[test]
1542 fn test_u64_add_scalar_large() {
1543 let t = FlexTensor::from_data(TensorData::new(vec![1u64, 2, 3], [3]));
1544 let big: u64 = (i64::MAX as u64) + 100;
1545 let result = Flex::int_add_scalar(t, burn_backend::Scalar::from(big));
1546 let data: Vec<u64> = result.into_data().try_into_vec().unwrap();
1547 assert_eq!(data, vec![big + 1, big + 2, big + 3]);
1548 }
1549
1550 #[test]
1551 fn test_u64_greater_elem_large() {
1552 let big: u64 = (i64::MAX as u64) + 100;
1553 let t = FlexTensor::from_data(TensorData::new(vec![big, big + 1, big - 1], [3]));
1554 let result = Flex::int_greater_elem(
1555 t,
1556 burn_backend::Scalar::from(big),
1557 burn_std::BoolStore::Native,
1558 );
1559 let data: Vec<bool> = result.into_data().try_into_vec().unwrap();
1560 assert_eq!(data, vec![false, true, false]);
1561 }
1562
1563 #[test]
1564 fn test_int_mask_fill_i32() {
1565 let t = FlexTensor::from_data(TensorData::new(vec![1i32, 2, 3, 4], [4]));
1566 let mask = FlexTensor::from_data(TensorData::new(vec![true, false, true, false], [4]));
1567 let result = Flex::int_mask_fill(t, mask, burn_backend::Scalar::from(0i64));
1568 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1569 assert_eq!(data, vec![0, 2, 0, 4]);
1570 }
1571
1572 #[test]
1573 fn test_int_mask_fill_i16() {
1574 let t = FlexTensor::from_data(TensorData::new(vec![10i16, 20, 30, 40], [4]));
1575 let mask = FlexTensor::from_data(TensorData::new(vec![false, true, false, true], [4]));
1576 let result = Flex::int_mask_fill(t, mask, burn_backend::Scalar::from(-1i64));
1577 let data: Vec<i16> = result.into_data().try_into_vec().unwrap();
1578 assert_eq!(data, vec![10, -1, 30, -1]);
1579 }
1580
1581 #[test]
1582 fn test_int_mask_fill_u8() {
1583 let t = FlexTensor::from_data(TensorData::new(vec![1u8, 2, 3, 4], [4]));
1584 let mask = FlexTensor::from_data(TensorData::new(vec![true, true, false, false], [4]));
1585 let result = Flex::int_mask_fill(t, mask, burn_backend::Scalar::from(255i64));
1586 let data: Vec<u8> = result.into_data().try_into_vec().unwrap();
1587 assert_eq!(data, vec![255, 255, 3, 4]);
1588 }
1589
1590 #[test]
1591 fn test_int_mask_fill_u32() {
1592 let t = FlexTensor::from_data(TensorData::new(vec![100u32, 200, 300], [3]));
1593 let mask = FlexTensor::from_data(TensorData::new(vec![true, false, true], [3]));
1594 let result = Flex::int_mask_fill(t, mask, burn_backend::Scalar::from(0i64));
1595 let data: Vec<u32> = result.into_data().try_into_vec().unwrap();
1596 assert_eq!(data, vec![0, 200, 0]);
1597 }
1598
1599 #[test]
1600 fn test_int_mask_where_i32() {
1601 let t = FlexTensor::from_data(TensorData::new(vec![1i32, 2, 3, 4], [4]));
1602 let mask = FlexTensor::from_data(TensorData::new(vec![true, false, true, false], [4]));
1603 let v = FlexTensor::from_data(TensorData::new(vec![10i32, 20, 30, 40], [4]));
1604 let result = Flex::int_mask_where(t, mask, v);
1605 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1606 assert_eq!(data, vec![10, 2, 30, 4]);
1607 }
1608
1609 #[test]
1610 fn test_int_mask_where_u8() {
1611 let t = FlexTensor::from_data(TensorData::new(vec![1u8, 2, 3, 4], [4]));
1612 let mask = FlexTensor::from_data(TensorData::new(vec![false, true, false, true], [4]));
1613 let v = FlexTensor::from_data(TensorData::new(vec![10u8, 20, 30, 40], [4]));
1614 let result = Flex::int_mask_where(t, mask, v);
1615 let data: Vec<u8> = result.into_data().try_into_vec().unwrap();
1616 assert_eq!(data, vec![1, 20, 3, 40]);
1617 }
1618
1619 #[test]
1620 fn test_int_gather_i32() {
1621 let t = FlexTensor::from_data(TensorData::new(vec![10i32, 20, 30, 40, 50, 60], [2, 3]));
1622 let indices = FlexTensor::from_data(TensorData::new(vec![2i64, 0, 1, 2], [2, 2]));
1623 let result = Flex::int_gather(1, t, indices);
1624 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1625 assert_eq!(data, vec![30, 10, 50, 60]);
1626 }
1627
1628 #[test]
1629 fn test_int_select_u16() {
1630 let t = FlexTensor::from_data(TensorData::new(vec![10u16, 20, 30, 40, 50, 60], [2, 3]));
1631 let indices = FlexTensor::from_data(TensorData::new(vec![0i64, 1], [2]));
1632 let result = Flex::int_select(t, 1, indices);
1633 let data: Vec<u16> = result.into_data().try_into_vec().unwrap();
1634 assert_eq!(data, vec![10, 20, 40, 50]);
1635 }
1636
1637 #[test]
1638 fn test_int_cumsum_i32() {
1639 let t = FlexTensor::from_data(TensorData::new(vec![1i32, 2, 3, 4], [4]));
1640 let result = Flex::int_cumsum(t, 0);
1641 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1642 assert_eq!(data, vec![1, 3, 6, 10]);
1643 }
1644
1645 #[test]
1646 fn test_int_cumprod_u8() {
1647 let t = FlexTensor::from_data(TensorData::new(vec![1u8, 2, 3, 4], [4]));
1648 let result = Flex::int_cumprod(t, 0);
1649 let data: Vec<u8> = result.into_data().try_into_vec().unwrap();
1650 assert_eq!(data, vec![1, 2, 6, 24]);
1651 }
1652
1653 #[test]
1654 fn test_int_cummin_i32() {
1655 let t = FlexTensor::from_data(TensorData::new(vec![3i32, 1, 4, 1, 5], [5]));
1656 let result = Flex::int_cummin(t, 0);
1657 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1658 assert_eq!(data, vec![3, 1, 1, 1, 1]);
1659 }
1660
1661 #[test]
1662 fn test_int_cummax_u16() {
1663 let t = FlexTensor::from_data(TensorData::new(vec![3u16, 1, 4, 1, 5], [5]));
1664 let result = Flex::int_cummax(t, 0);
1665 let data: Vec<u16> = result.into_data().try_into_vec().unwrap();
1666 assert_eq!(data, vec![3, 3, 4, 4, 5]);
1667 }
1668
1669 #[test]
1670 fn test_int_scatter_add_i32() {
1671 let t = FlexTensor::from_data(TensorData::new(vec![0i32, 0, 0], [1, 3]));
1672 let indices = FlexTensor::from_data(TensorData::new(vec![0i64, 2, 1], [1, 3]));
1673 let values = FlexTensor::from_data(TensorData::new(vec![10i32, 20, 30], [1, 3]));
1674 let result = Flex::int_scatter(
1675 1,
1676 t,
1677 indices,
1678 values,
1679 burn_backend::tensor::IndexingUpdateOp::Add,
1680 );
1681 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1682 assert_eq!(data, vec![10, 30, 20]);
1683 }
1684
1685 #[test]
1686 fn test_int_select_add_u8() {
1687 let t = FlexTensor::from_data(TensorData::new(vec![1u8, 2, 3], [3]));
1688 let indices = FlexTensor::from_data(TensorData::new(vec![0i64, 2], [2]));
1689 let values = FlexTensor::from_data(TensorData::new(vec![10u8, 20], [2]));
1690 let result = Flex::int_select_assign(
1691 t,
1692 0,
1693 indices,
1694 values,
1695 burn_backend::tensor::IndexingUpdateOp::Add,
1696 );
1697 let data: Vec<u8> = result.into_data().try_into_vec().unwrap();
1698 assert_eq!(data, vec![11, 2, 23]);
1699 }
1700
1701 #[test]
1702 fn test_int_random_i32() {
1703 use burn_backend::{DType, Distribution, ops::IntTensorOps};
1704 use burn_std::{IntDType, Shape};
1705
1706 let shape = Shape::from(vec![100]);
1707 let dist = Distribution::Uniform(0.0, 10.0);
1708 let device = crate::FlexDevice;
1709 let t = Flex::int_random(shape, dist, &device, IntDType::I32);
1710 assert_eq!(t.dtype(), DType::I32);
1711 let data: Vec<i32> = t.into_data().try_into_vec().unwrap();
1712 assert!(data.iter().all(|&v| (0..=10).contains(&v)));
1713 }
1714
1715 #[test]
1716 fn test_int_random_u8() {
1717 use burn_backend::{DType, Distribution, ops::IntTensorOps};
1718 use burn_std::{IntDType, Shape};
1719
1720 let shape = Shape::from(vec![50]);
1721 let dist = Distribution::Uniform(0.0, 100.0);
1722 let device = crate::FlexDevice;
1723 let t = Flex::int_random(shape, dist, &device, IntDType::U8);
1724 assert_eq!(t.dtype(), DType::U8);
1725 }
1726
1727 #[test]
1728 fn test_int_mean_i32() {
1729 use burn_backend::{DType, ops::IntTensorOps};
1730
1731 let t = FlexTensor::from_data(TensorData::new(vec![10i32, 20, 30], [3]));
1732 let result = Flex::int_mean(t);
1733 assert_eq!(result.dtype(), DType::I32);
1734 let data: Vec<i32> = result.into_data().try_into_vec().unwrap();
1735 assert_eq!(data, vec![20]); }
1737}