1use crate::{
2 CubeDevice,
3 kernel::utils::{address_type, shape_divmod},
4};
5use crate::{element::CubeElement, tensor::CubeTensor};
6use crate::{
7 kernel::{
8 AddOp, BitwiseAndOp, BitwiseOrOp, BitwiseXorOp, DivOp, MulOp, PowOp, RemainderOp, SubOp,
9 launch_binop, launch_binop_int, launch_scalar_binop, launch_scalar_binop_int,
10 },
11 ops::max_vector_size,
12};
13use burn_backend::cubecl::dtype_to_storage_type;
14use burn_backend::{DType, Shape, TensorMetadata};
15use burn_std::Metadata;
16use cubecl::{
17 calculate_cube_count_elemwise,
18 ir::{ElemType, dialect::math::IsNanOp},
19 prelude::*,
20 std::tensor::layout::linear::LinearViewMut,
21};
22use cubecl::{client::Client, server::MemoryLayout};
23use cubecl::{server::MemoryLayoutDescriptor, std::FastDivmod};
24
25pub fn full<E: CubeElement>(shape: Shape, device: &CubeDevice, value: E) -> CubeTensor {
27 let client = device.client();
28
29 full_client::<E>(client, shape, device.clone(), value)
30}
31
32pub fn full_client<E: CubeElement>(
34 client: Client,
35 shape: Shape,
36 device: CubeDevice,
37 value: E,
38) -> CubeTensor {
39 let dtype = E::dtype();
40 full_device_dtype(
41 client,
42 shape,
43 device,
44 InputScalar::new(value, dtype_to_storage_type(dtype)),
45 dtype,
46 )
47}
48
49pub fn full_device_dtype(
51 client: Client,
52 shape: Shape,
53 device: CubeDevice,
54 value: InputScalar,
55 dtype: DType,
56) -> CubeTensor {
57 let empty = empty_device_dtype(client, device, shape, dtype);
58
59 fill_device_dtype(empty, value)
60}
61
62pub(crate) fn fill_device_dtype(tensor: CubeTensor, value: InputScalar) -> CubeTensor {
64 #[cube(launch_unchecked, address_type = "dynamic")]
65 pub fn full_kernel<C: Numeric, N: Size>(
66 mut tensor: LinearViewMut<'_, Vector<C, N>>,
67 value: InputScalar,
68 #[define(C)] _dtype: ElemType,
69 ) {
70 if !tensor.is_in_bounds(ABSOLUTE_POS) {
71 terminate!();
72 }
73
74 tensor.write(ABSOLUTE_POS, Vector::new(value.get::<C>()));
75 }
76
77 let num_elems = tensor.meta.num_elements();
78 let vector_size = max_vector_size(&tensor);
79
80 let working_units = num_elems / vector_size as usize;
81 let cube_dim = CubeDim::new(&tensor.client, working_units);
82 let cube_count = calculate_cube_count_elemwise(&tensor.client, working_units, cube_dim);
83
84 unsafe {
85 full_kernel::launch_unchecked(
86 &tensor.client,
87 cube_count,
88 cube_dim,
89 address_type!(tensor),
90 vector_size,
91 tensor.clone().into_linear_view(),
92 value,
93 dtype_to_storage_type(tensor.dtype),
94 );
95 }
96
97 tensor
98}
99
100pub fn zeros(device: CubeDevice, shape: Shape, dtype: DType) -> CubeTensor {
102 let client = device.client();
103 full_device_dtype(
104 client,
105 shape,
106 device,
107 InputScalar::new(0u32, dtype_to_storage_type(dtype)),
108 dtype,
109 )
110}
111
112pub fn ones(device: CubeDevice, shape: Shape, dtype: DType) -> CubeTensor {
114 let client = device.client();
115 full_device_dtype(
116 client,
117 shape,
118 device,
119 InputScalar::new(1u32, dtype_to_storage_type(dtype)),
120 dtype,
121 )
122}
123
124pub fn zeros_client(client: Client, device: CubeDevice, shape: Shape, dtype: DType) -> CubeTensor {
126 full_device_dtype(
127 client,
128 shape,
129 device,
130 InputScalar::new(0u32, dtype_to_storage_type(dtype)),
131 dtype,
132 )
133}
134
135pub fn ones_client(client: Client, device: CubeDevice, shape: Shape, dtype: DType) -> CubeTensor {
137 full_device_dtype(
138 client,
139 shape,
140 device,
141 InputScalar::new(1u32, dtype_to_storage_type(dtype)),
142 dtype,
143 )
144}
145
146pub fn empty_device<E: CubeElement>(
148 client: Client,
149 device: CubeDevice,
150 shape: Shape,
151) -> CubeTensor {
152 let MemoryLayout { memory, strides } = client.empty_tensor(shape.clone(), size_of::<E>());
153
154 CubeTensor::new(
155 client,
156 memory,
157 Metadata::new(shape, strides),
158 device,
159 E::dtype(),
160 )
161}
162
163pub fn empty_device_dtype(
165 client: Client,
166 device: CubeDevice,
167 shape: Shape,
168 dtype: DType,
169) -> CubeTensor {
170 let MemoryLayout { memory, strides } = client.empty_tensor(shape.clone(), dtype.size());
171
172 CubeTensor::new(client, memory, Metadata::new(shape, strides), device, dtype)
173}
174
175pub fn empty_device_contiguous_dtype(
177 client: Client,
178 device: CubeDevice,
179 shape: Shape,
180 dtype: DType,
181) -> CubeTensor {
182 let descriptor = MemoryLayoutDescriptor::contiguous(shape.clone(), dtype.size());
183 let MemoryLayout { memory, strides } = client.empty_tensors(vec![descriptor]).remove(0);
184
185 CubeTensor::new(client, memory, Metadata::new(shape, strides), device, dtype)
186}
187
188pub fn add(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
190 launch_binop::<AddOp>(lhs, rhs)
191}
192
193pub fn add_scalar(lhs: CubeTensor, rhs: InputScalar) -> CubeTensor {
195 launch_scalar_binop::<AddOp>(lhs, rhs)
196}
197
198pub fn sub(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
200 launch_binop::<SubOp>(lhs, rhs)
201}
202
203pub fn sub_scalar(lhs: CubeTensor, rhs: InputScalar) -> CubeTensor {
205 launch_scalar_binop::<SubOp>(lhs, rhs)
206}
207
208pub fn mul(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
210 launch_binop::<MulOp>(lhs, rhs)
211}
212
213pub fn mul_scalar(lhs: CubeTensor, rhs: InputScalar) -> CubeTensor {
215 launch_scalar_binop::<MulOp>(lhs, rhs)
216}
217
218pub fn div(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
220 launch_binop::<DivOp>(lhs, rhs)
221}
222
223pub fn div_scalar(lhs: CubeTensor, rhs: InputScalar) -> CubeTensor {
225 launch_scalar_binop::<DivOp>(lhs, rhs)
226}
227
228pub fn remainder(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
230 launch_binop::<RemainderOp>(lhs, rhs)
231}
232
233pub fn remainder_scalar(lhs: CubeTensor, rhs: InputScalar) -> CubeTensor {
235 launch_scalar_binop::<RemainderOp>(lhs, rhs)
236}
237
238pub fn pow(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
240 launch_binop::<PowOp>(lhs, rhs)
241}
242
243pub fn bitwise_and(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
245 launch_binop_int::<BitwiseAndOp>(lhs, rhs)
246}
247
248pub fn bitwise_and_scalar(lhs: CubeTensor, rhs: InputScalar) -> CubeTensor {
250 launch_scalar_binop_int::<BitwiseAndOp>(lhs, rhs)
251}
252
253pub fn bitwise_or(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
255 launch_binop_int::<BitwiseOrOp>(lhs, rhs)
256}
257
258pub fn bitwise_or_scalar(lhs: CubeTensor, rhs: InputScalar) -> CubeTensor {
260 launch_scalar_binop_int::<BitwiseOrOp>(lhs, rhs)
261}
262
263pub fn bitwise_xor(lhs: CubeTensor, rhs: CubeTensor) -> CubeTensor {
265 launch_binop_int::<BitwiseXorOp>(lhs, rhs)
266}
267
268pub fn bitwise_xor_scalar(lhs: CubeTensor, rhs: InputScalar) -> CubeTensor {
270 launch_scalar_binop_int::<BitwiseXorOp>(lhs, rhs)
271}
272
273pub(crate) trait CumulativeOpFamily: Send + Sync + 'static {
275 type CumulativeOp<C: Numeric>: CumulativeOp<C>;
276}
277
278#[cube]
280pub(crate) trait CumulativeOp<C: Numeric>: 'static + Send + Sync {
281 fn execute(lhs: C, rhs: C) -> C;
283
284 fn init_value(first_element: C) -> C;
286}
287
288struct SumOp;
290struct ProdOp;
291struct MaxOp;
292struct MinOp;
293
294#[cube]
297fn numeric_is_nan<N: Numeric>(value: N) -> bool {
298 intrinsic!(|scope| {
299 let is_nan = IsNanOp::new(scope.ctx_mut(), value.read_value(scope));
300 scope.register_with_result(&is_nan).into()
301 })
302}
303
304#[cube]
305fn cumulative_max<N: Numeric>(lhs: N, rhs: N) -> N {
306 let elem_type = elem_type_of::<N>();
307 if comptime!(elem_type.is_float()) {
308 if numeric_is_nan::<N>(lhs) {
309 lhs
310 } else if numeric_is_nan::<N>(rhs) {
311 rhs
312 } else {
313 max(lhs, rhs)
314 }
315 } else {
316 max(lhs, rhs)
317 }
318}
319
320#[cube]
321fn cumulative_min<N: Numeric>(lhs: N, rhs: N) -> N {
322 let elem_type = elem_type_of::<N>();
323 if comptime!(elem_type.is_float()) {
324 if numeric_is_nan::<N>(lhs) {
325 lhs
326 } else if numeric_is_nan::<N>(rhs) {
327 rhs
328 } else {
329 min(lhs, rhs)
330 }
331 } else {
332 min(lhs, rhs)
333 }
334}
335
336impl CumulativeOpFamily for SumOp {
338 type CumulativeOp<C: Numeric> = Self;
339}
340
341impl CumulativeOpFamily for ProdOp {
342 type CumulativeOp<C: Numeric> = Self;
343}
344
345impl CumulativeOpFamily for MaxOp {
346 type CumulativeOp<C: Numeric> = Self;
347}
348
349impl CumulativeOpFamily for MinOp {
350 type CumulativeOp<C: Numeric> = Self;
351}
352
353#[cube]
355impl<N: Numeric> CumulativeOp<N> for SumOp {
356 fn execute(lhs: N, rhs: N) -> N {
357 lhs + rhs
358 }
359
360 fn init_value(_first_element: N) -> N {
361 N::zero()
362 }
363}
364
365#[cube]
366impl<N: Numeric> CumulativeOp<N> for ProdOp {
367 fn execute(lhs: N, rhs: N) -> N {
368 lhs * rhs
369 }
370
371 fn init_value(_first_element: N) -> N {
372 N::from_int(1)
373 }
374}
375
376#[cube]
377impl<N: Numeric> CumulativeOp<N> for MaxOp {
378 fn execute(lhs: N, rhs: N) -> N {
379 cumulative_max::<N>(lhs, rhs)
380 }
381
382 fn init_value(first_element: N) -> N {
383 first_element
384 }
385}
386
387#[cube]
388impl<N: Numeric> CumulativeOp<N> for MinOp {
389 fn execute(lhs: N, rhs: N) -> N {
390 cumulative_min::<N>(lhs, rhs)
391 }
392
393 fn init_value(first_element: N) -> N {
394 first_element
395 }
396}
397
398#[cube(launch_unchecked, address_type = "dynamic")]
412fn cumulative_kernel<C: Numeric, O: CumulativeOpFamily>(
413 input: &Tensor<C>,
414 mut output: LinearViewMut<'_, C>,
415 shape: Sequence<FastDivmod<usize>>,
416 #[comptime] dim: usize,
417 #[define(C)] _dtype: ElemType,
418) {
419 if !output.is_in_bounds(ABSOLUTE_POS) {
420 terminate!();
421 }
422
423 let rank = comptime![shape.len()];
424 let dim_stride = input.stride(dim);
425
426 let mut remainder = ABSOLUTE_POS;
427 let mut offset = 0;
428 let mut dim_idx = 0;
429
430 #[unroll]
431 for i in 0..shape.len() {
432 let i = comptime![rank - i - 1];
433 let (rem, local_idx) = shape.index(i).div_mod(remainder);
434 remainder = rem;
435 if i == dim {
436 dim_idx = local_idx;
437 } else {
438 offset += local_idx * input.stride(i);
439 }
440 }
441
442 let first_read_idx = offset + dim_idx * dim_stride;
444 let first_elem = input[first_read_idx];
445
446 let mut result = O::CumulativeOp::<C>::init_value(first_elem);
448
449 for i in 0..=dim_idx {
451 let read_idx = offset + i * dim_stride;
452 result = O::CumulativeOp::<C>::execute(result, input[read_idx]);
453 }
454 output.write(ABSOLUTE_POS, result);
455}
456
457pub fn cumsum(input: CubeTensor, dim: usize) -> CubeTensor {
459 cumulative_op::<SumOp>(input, dim)
460}
461
462pub fn cumprod(input: CubeTensor, dim: usize) -> CubeTensor {
464 cumulative_op::<ProdOp>(input, dim)
465}
466
467pub fn cummin(input: CubeTensor, dim: usize) -> CubeTensor {
469 cumulative_op::<MinOp>(input, dim)
470}
471
472pub fn cummax(input: CubeTensor, dim: usize) -> CubeTensor {
474 cumulative_op::<MaxOp>(input, dim)
475}
476
477fn cumulative_op<O: CumulativeOpFamily>(input: CubeTensor, dim: usize) -> CubeTensor {
479 let client = input.client.clone();
480 let device = input.device.clone();
481
482 let output = empty_device_dtype(client.clone(), device, input.shape(), input.dtype);
483
484 let num_elems = output.meta.num_elements();
485 let working_units = num_elems;
486 let cube_dim = CubeDim::new(&client, working_units);
487 let cube_count = calculate_cube_count_elemwise(&client, working_units, cube_dim);
488 let shape = shape_divmod(&input);
489
490 unsafe {
491 cumulative_kernel::launch_unchecked::<O>(
492 &client,
493 cube_count,
494 cube_dim,
495 address_type!(input, output),
496 input.into_tensor_arg(),
497 output.clone().into_linear_view(),
498 shape,
499 dim,
500 dtype_to_storage_type(output.dtype),
501 );
502 }
503
504 output
505}