cubek_convolution/kernels/backward_data/
args.rs1use cubecl::{
2 client::Client,
3 prelude::*,
4 std::tensor::{
5 launch::ViewArg,
6 layout::{
7 VirtualLayoutLaunch,
8 chain::{Chain, ChainLaunch},
9 },
10 },
11 zspace::{shape, strides},
12};
13use cubecl_ir::EnumSet;
14use cubek_matmul::{
15 definition::MatmulElems,
16 multi_level::{
17 BatchMatmulRoutine,
18 args::*,
19 components::global::memory::{GlobalLayoutConfig, NoopLayout, NoopLayoutLaunch},
20 definition::{BatchMatmulBlueprint, Blueprint},
21 },
22};
23use cubek_std::{InputBinding, MatrixLayout, launch::tma::remap_storage_for_tma};
24
25use crate::components::{
26 ConvolutionParams, ConvolutionProblem,
27 global::{
28 args::{RuntimeArgs, RuntimeArgsLaunch},
29 layout::{
30 Im2colLayout, Im2colLayoutLaunch, NhwcCheck, NhwcLayout, NhwcLayoutLaunch, OutLayout,
31 OutLayoutLaunch, TmaIm2colLayout, TmaIm2colLayoutLaunch, WeightLayout,
32 WeightLayoutLaunch,
33 },
34 },
35};
36
37pub trait ConcreteArgs<A: BatchMatmulRoutine<RuntimeArgs>>:
38 MatmulArgs<
39 Input<Vector<Lhs, LhsSize>, Vector<Rhs, RhsSize>, Vector<Acc, AccSize>>: ConcreteInputsFactory<A>,
40 Output<Vector<Acc, AccSize>>: ConcreteOutputFactory<A>,
41 Config = RuntimeArgs,
42 >
43{
44 fn adjust_problem(
45 client: &Client,
46 problem: ConvolutionProblem,
47 selection: &A::Blueprint,
48 dtypes: &MatmulElems,
49 ) -> ConvolutionProblem;
50}
51
52impl<A: BatchMatmulRoutine<RuntimeArgs>> ConcreteArgs<A> for TensorArgs<RuntimeArgs> {
53 fn adjust_problem(
54 client: &Client,
55 mut problem: ConvolutionProblem,
56 _blueprint: &A::Blueprint,
57 dtypes: &MatmulElems,
58 ) -> ConvolutionProblem {
59 let io_width = client.properties().io_width();
60 let channel_align = io_width as usize / dtypes.lhs_global.size_bits();
61 let padded_channels = problem.out_channels.next_multiple_of(channel_align);
62 let shape_k = problem.kernel_size.iter().product::<u32>() as usize * padded_channels;
63
64 problem.k = shape_k;
65 problem.padded_channels = padded_channels;
66
67 problem
68 }
69}
70
71impl<A: BatchMatmulRoutine<RuntimeArgs, Blueprint = BatchMatmulBlueprint>> ConcreteArgs<A>
72 for TensorMapArgs<RuntimeArgs>
73{
74 fn adjust_problem(
75 _client: &Client,
76 mut problem: ConvolutionProblem,
77 blueprint: &BatchMatmulBlueprint,
78 _dtypes: &MatmulElems,
79 ) -> ConvolutionProblem {
80 let channel_align = blueprint.tiling_scheme.tile_size.k() as usize;
81 let padded_channels = problem.out_channels.next_multiple_of(channel_align);
82 let shape_k = problem.kernel_size.iter().product::<u32>() as usize * padded_channels;
83
84 problem.k = shape_k;
85 problem.padded_channels = padded_channels;
86
87 problem
88 }
89}
90
91pub trait ConcreteInputsFactory<A: BatchMatmulRoutine<RuntimeArgs>>: LaunchArg {
94 #[allow(clippy::too_many_arguments)]
95 fn create(
96 out_grad: InputBinding,
97 weights: InputBinding,
98 blueprint: &A::Blueprint,
99 problem: &ConvolutionProblem,
100 dtypes: &MatmulElems,
101 ) -> (Self::RuntimeArg, RuntimeArgsLaunch);
102}
103
104pub trait ConcreteOutputFactory<A: BatchMatmulRoutine<RuntimeArgs>>: LaunchArg {
107 fn create(
108 out: TensorBinding,
109 blueprint: &A::Blueprint,
110 problem: &ConvolutionProblem,
111 ) -> Self::RuntimeArg;
112}
113
114impl<Lhs: CubePrimitive, Rhs: CubePrimitive, EO: CubePrimitive, A: BatchMatmulRoutine<RuntimeArgs>>
115 ConcreteInputsFactory<A> for TensorInputs<Lhs, Rhs, EO>
116{
117 fn create(
118 out_grad: InputBinding,
119 weights: InputBinding,
120 blueprint: &A::Blueprint,
121 problem: &ConvolutionProblem,
122 _dtypes: &MatmulElems,
123 ) -> (Self::RuntimeArg, RuntimeArgsLaunch) {
124 type LhsLayout = Chain<NhwcLayout, Im2colLayout>;
125 type RhsLayout = Chain<NhwcLayout, WeightLayout>;
126
127 let padded_channels = problem.padded_channels as u32;
128 let params = ConvolutionParams::from_problem(problem);
129
130 let layout_lhs =
131 Im2colLayoutLaunch::from_args(problem, params, blueprint.lhs_global_layout_config());
132 let layout_rhs =
133 WeightLayoutLaunch::from_args(problem, blueprint.rhs_global_layout_config());
134
135 let layout_lhs = {
136 let mut checks = EnumSet::empty();
137 if problem.should_check_spatial_bounds() {
138 checks.insert(NhwcCheck::Spatial);
139 }
140 if problem.should_check_channel() {
141 checks.insert(NhwcCheck::Channel);
142 }
143 let global = NhwcLayoutLaunch::checked(checks);
144 ChainLaunch::new(global, layout_lhs)
145 };
146 let layout_rhs = {
147 let mut checks = EnumSet::empty();
148 if problem.should_check_channel() {
149 checks.insert(NhwcCheck::Batch);
150 }
151 let global = NhwcLayoutLaunch::checked(checks);
152 ChainLaunch::new(global, layout_rhs)
153 };
154
155 let inputs = TensorInputsLaunch::new(
156 VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new()),
157 ViewArg::new_tensor::<LhsLayout>(out_grad.into_data().into_tensor_arg(), layout_lhs),
158 VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new()),
159 ViewArg::new_tensor::<RhsLayout>(weights.into_data().into_tensor_arg(), layout_rhs),
160 ComptimeOptionArgs::None,
161 ComptimeOptionArgs::None,
162 );
163
164 let runtime_args = RuntimeArgsLaunch::new(
165 problem.k as u32,
166 problem.out_channels as u32,
167 padded_channels,
168 problem.operation,
169 );
170
171 (inputs, runtime_args)
172 }
173}
174
175impl<EG: CubePrimitive, A: BatchMatmulRoutine<RuntimeArgs>> ConcreteOutputFactory<A>
176 for TensorOutput<EG>
177{
178 fn create(
179 out: TensorBinding,
180 blueprint: &A::Blueprint,
181 problem: &ConvolutionProblem,
182 ) -> Self::RuntimeArg {
183 type Layout = Chain<NhwcLayout, OutLayout>;
184
185 let global = NhwcLayoutLaunch::unchecked();
186 let layout = OutLayoutLaunch::from_args(problem, blueprint.out_global_layout_config());
187 let layout = ChainLaunch::new(global, layout);
188 let view = ViewArg::new_tensor::<Layout>(out.into_tensor_arg(), layout);
189 let batch = VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new());
190 TensorOutputLaunch::new(view, batch)
191 }
192}
193
194impl<
195 Lhs: CubePrimitive,
196 Rhs: CubePrimitive,
197 EO: CubePrimitive,
198 A: BatchMatmulRoutine<RuntimeArgs, Blueprint = BatchMatmulBlueprint>,
199> ConcreteInputsFactory<A> for TensorMapInputs<Lhs, Rhs, EO>
200{
201 fn create(
202 out_grad: InputBinding,
203 weights: InputBinding,
204 blueprint: &BatchMatmulBlueprint,
205 problem: &ConvolutionProblem,
206 dtypes: &MatmulElems,
207 ) -> (Self::RuntimeArg, RuntimeArgsLaunch) {
208 type LhsLayout = TmaIm2colLayout;
209 type RhsLayout = WeightLayout;
210
211 let tiling_scheme = blueprint.tiling_scheme;
212 let stage_m = tiling_scheme.elements_per_stage_along_m();
213 let stage_n = tiling_scheme.elements_per_stage_along_n();
214 let stage_k = tiling_scheme.elements_per_stage_along_k();
215 let tile_size_k = tiling_scheme.tile_size.k;
216
217 let mut stage_size_rhs = shape![1; problem.dimensionality.num_dims()];
218 stage_size_rhs.insert(0, stage_k as usize);
219 stage_size_rhs.push(stage_n as usize);
220
221 let lhs_elem = remap_storage_for_tma(dtypes.lhs_stage);
222
223 let mut elem_stride = strides![1; 2 + problem.stride.len()];
224
225 for (i, stride) in problem.stride.iter().enumerate() {
226 elem_stride[i + 1] = *stride as usize;
227 }
228
229 let lhs = TensorMapArg::new(
230 Im2colArgs {
231 pixel_box_lower_corner: calculate_lower_corner(problem),
232 pixel_box_upper_corner: calculate_upper_corner(problem),
233 channels_per_pixel: tile_size_k,
234 pixels_per_column: stage_m,
235 },
236 out_grad.into_data().into_tensor_arg(),
237 lhs_elem,
238 )
239 .with_elem_stride(elem_stride);
240
241 let rhs = TensorMapArg::new(
242 TiledArgs {
243 tile_size: stage_size_rhs,
244 },
245 weights.into_data().into_tensor_arg(),
246 dtypes.rhs_global,
247 );
248
249 let padded_channels = problem.padded_channels as u32;
250 let shape_k = problem.k as u32;
251
252 let stages_lhs = A::num_stages().lhs;
256 let stages_size_k = blueprint.tiling_scheme.elements_per_stage_along_k() * stages_lhs;
257 let check_kernel = !shape_k.is_multiple_of(stages_size_k);
258 let lhs_layout = TmaIm2colLayoutLaunch::from_args(problem, check_kernel);
259 let rhs_layout = WeightLayoutLaunch::from_args(
260 problem,
261 GlobalLayoutConfig {
262 check_row_bounds: false,
263 check_col_bounds: false,
264 matrix_layout: MatrixLayout::default(),
265 },
266 );
267
268 let inputs = TensorMapInputsLaunch::new(
269 ViewArg::new_tensor_map_im2col::<LhsLayout, _, _>(lhs, lhs_layout),
270 ViewArg::new_tensor_map_tiled::<RhsLayout>(rhs, rhs_layout),
271 ComptimeOptionArgs::None,
272 ComptimeOptionArgs::None,
273 );
274
275 let runtime_args = RuntimeArgsLaunch::new(
276 shape_k,
277 problem.out_channels as u32,
278 padded_channels,
279 problem.operation,
280 );
281
282 (inputs, runtime_args)
283 }
284}
285
286#[allow(clippy::needless_range_loop)]
287fn calculate_lower_corner(problem: &ConvolutionProblem) -> Vec<i32> {
288 let mut out = vec![0; problem.padding.len()];
289 for i in 0..problem.padding.len() {
290 out[i] =
291 problem.padding[i] - (problem.kernel_size[i] as i32 - 1) * problem.dilation[i] as i32;
292 }
293 out
294}
295
296#[allow(clippy::needless_range_loop)]
297fn calculate_upper_corner(problem: &ConvolutionProblem) -> Vec<i32> {
298 let mut out = vec![0; problem.padding.len()];
299 for i in 0..problem.padding.len() {
300 out[i] = problem.padding[i]
301 - (problem.kernel_size[i] as i32 - 1) * problem.dilation[i] as i32
302 + problem.in_shape[i] as i32
303 - problem.out_shape[i] as i32;
304 }
305 out
306}