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