1use crate::{DeviceBackend, DeviceRuntime, FloatElement, IntElement, element::BoolElement};
2use rudnn::convolution::tensor::ConvTranspose2dStrategy;
3use ruda_tensor::tensor::{BoolTensor, FloatTensor, IntTensor};
4use ruda_tensor::{
5 TensorMetadata,
6 ops::{
7 AttentionModuleOptions, ConvOptions, ConvTransposeOptions, DeformConv2dBackward,
8 DeformConvOptions, InterpolateOptions, MaxPool2dBackward, MaxPool2dWithIndices, ModuleOps,
9 },
10};
11
12fn norm_buffer<R: DeviceRuntime>(tensor: crate::RudaTensor<R>) -> ruda::runtime::normalization::TensorBuffer {
13 ruda::runtime::normalization::TensorBuffer {
14 shape: tensor.meta.shape().clone(), strides: tensor.meta.strides().clone(),
15 handle: tensor.handle, dtype: tensor.dtype,
16 }
17}
18
19fn norm_tensor<R: DeviceRuntime>(
20 buffer: ruda::runtime::normalization::TensorBuffer,
21 client: ruda::runtime::client::ComputeClient<R>, device: R::Device,
22) -> crate::RudaTensor<R> {
23 crate::RudaTensor::new(client, buffer.handle,
24 ruda_core::tensor::Metadata::new(buffer.shape, buffer.strides), device, buffer.dtype)
25}
26
27impl<R, F, I, BT> ModuleOps<Self> for DeviceBackend<R, F, I, BT>
28where
29 R: DeviceRuntime,
30 F: FloatElement,
31 I: IntElement,
32 BT: BoolElement,
33{
34 fn has_layer_norm_backward() -> bool { R::has_native_layer_norm() }
35
36 fn layer_norm(
37 tensor: FloatTensor<Self>, gamma: FloatTensor<Self>,
38 beta: Option<FloatTensor<Self>>, epsilon: f64,
39 ) -> FloatTensor<Self> {
40 if R::has_native_layer_norm() {
41 Self::layer_norm_with_stats(tensor, gamma, beta, epsilon).output
42 } else {
43 Self::layer_norm_default(tensor, gamma, beta, epsilon)
44 }
45 }
46
47 fn layer_norm_with_stats(
48 tensor: FloatTensor<Self>, gamma: FloatTensor<Self>,
49 beta: Option<FloatTensor<Self>>, epsilon: f64,
50 ) -> ruda_tensor::ops::LayerNormOutput<Self> {
51 let client = tensor.client.clone();
52 let device = tensor.device.clone();
53 for other in core::iter::once(&gamma).chain(beta.iter()) {
54 assert_eq!(device, other.device, "LayerNorm device mismatch");
55 assert!(client.same_execution_queue(&other.client), "LayerNorm queue mismatch");
56 }
57 let [output, mean, rstd] = R::layer_norm(
58 &client, norm_buffer(tensor), norm_buffer(gamma), beta.map(norm_buffer), epsilon,
59 );
60 let from = |buffer| norm_tensor(buffer, client.clone(), device.clone());
61 ruda_tensor::ops::LayerNormOutput { output: from(output), mean: from(mean), rstd: from(rstd) }
62 }
63
64 fn layer_norm_backward(
65 tensor: FloatTensor<Self>, gamma: FloatTensor<Self>, grad: FloatTensor<Self>,
66 mean: FloatTensor<Self>, rstd: FloatTensor<Self>,
67 ) -> ruda_tensor::ops::LayerNormBackward<Self> {
68 let client = tensor.client.clone();
69 let device = tensor.device.clone();
70 for other in [&gamma, &grad, &mean, &rstd] {
71 assert_eq!(device, other.device, "LayerNorm backward device mismatch");
72 assert!(client.same_execution_queue(&other.client), "LayerNorm backward queue mismatch");
73 }
74 let [input, weight, bias] = R::layer_norm_backward(
75 &client, norm_buffer(tensor), norm_buffer(gamma), norm_buffer(grad),
76 norm_buffer(mean), norm_buffer(rstd),
77 );
78 let from = |buffer| norm_tensor(buffer, client.clone(), device.clone());
79 ruda_tensor::ops::LayerNormBackward { input: from(input), weight: from(weight), bias: from(bias) }
80 }
81
82 fn conv1d(
83 x: FloatTensor<Self>,
84 weight: FloatTensor<Self>,
85 bias: Option<FloatTensor<Self>>,
86 options: ConvOptions<1>,
87 ) -> FloatTensor<Self> {
88 rudnn::convolution::tensor::conv_forward::<R, 1>(x, weight, bias, options, Default::default()).unwrap()
89 }
90
91 fn conv1d_x_backward(
92 x: FloatTensor<Self>,
93 weight: FloatTensor<Self>,
94 output_grad: FloatTensor<Self>,
95 options: ConvOptions<1>,
96 ) -> FloatTensor<Self> {
97 rudnn::convolution::tensor::conv_data_backward(
98 output_grad,
99 weight,
100 x.shape(),
101 options,
102 Default::default(),
103 )
104 .unwrap()
105 }
106
107 fn conv1d_weight_backward(
108 x: FloatTensor<Self>,
109 weight: FloatTensor<Self>,
110 output_grad: FloatTensor<Self>,
111 options: ConvOptions<1>,
112 ) -> FloatTensor<Self> {
113 rudnn::convolution::tensor::conv_weight_backward::<R, 1>(
114 x,
115 output_grad,
116 weight.shape(),
117 options,
118 Default::default(),
119 )
120 .unwrap()
121 }
122
123 fn conv2d(
124 x: FloatTensor<Self>,
125 weight: FloatTensor<Self>,
126 bias: Option<FloatTensor<Self>>,
127 options: ConvOptions<2>,
128 ) -> FloatTensor<Self> {
129 rudnn::convolution::tensor::conv_forward::<R, 2>(x, weight, bias, options, Default::default()).unwrap()
130 }
131
132 fn conv2d_x_backward(
133 x: FloatTensor<Self>,
134 weight: FloatTensor<Self>,
135 output_grad: FloatTensor<Self>,
136 options: ConvOptions<2>,
137 ) -> FloatTensor<Self> {
138 rudnn::convolution::tensor::conv_data_backward(
139 output_grad,
140 weight,
141 x.shape(),
142 options,
143 Default::default(),
144 )
145 .unwrap()
146 }
147
148 fn conv2d_weight_backward(
149 x: FloatTensor<Self>,
150 weight: FloatTensor<Self>,
151 output_grad: FloatTensor<Self>,
152 options: ConvOptions<2>,
153 ) -> FloatTensor<Self> {
154 rudnn::convolution::tensor::conv_weight_backward::<R, 2>(
155 x,
156 output_grad,
157 weight.shape(),
158 options,
159 Default::default(),
160 )
161 .unwrap()
162 }
163
164 fn deform_conv2d(
165 x: FloatTensor<Self>,
166 offset: FloatTensor<Self>,
167 weight: FloatTensor<Self>,
168 mask: Option<FloatTensor<Self>>,
169 bias: Option<FloatTensor<Self>>,
170 options: DeformConvOptions<2>,
171 ) -> FloatTensor<Self> {
172 rudnn::convolution::tensor::deform_conv2d(x, offset, weight, mask, bias, options).unwrap()
173 }
174
175 fn deform_conv2d_backward(
176 x: FloatTensor<Self>,
177 offset: FloatTensor<Self>,
178 weight: FloatTensor<Self>,
179 mask: Option<FloatTensor<Self>>,
180 bias: Option<FloatTensor<Self>>,
181 output_grad: FloatTensor<Self>,
182 options: DeformConvOptions<2>,
183 ) -> DeformConv2dBackward<Self> {
184 let (x, o, w, m, b) = rudnn::convolution::tensor::deform_conv2d_backward(
185 x,
186 offset,
187 weight,
188 mask,
189 bias,
190 output_grad,
191 options,
192 )
193 .unwrap();
194 DeformConv2dBackward::new(x, o, w, m, b)
195 }
196
197 fn conv3d(
198 x: FloatTensor<Self>,
199 weight: FloatTensor<Self>,
200 bias: Option<FloatTensor<Self>>,
201 options: ConvOptions<3>,
202 ) -> FloatTensor<Self> {
203 rudnn::convolution::tensor::conv_forward::<R, 3>(x, weight, bias, options, Default::default()).unwrap()
204 }
205
206 fn conv3d_x_backward(
207 x: FloatTensor<Self>,
208 weight: FloatTensor<Self>,
209 output_grad: FloatTensor<Self>,
210 options: ConvOptions<3>,
211 ) -> FloatTensor<Self> {
212 rudnn::convolution::tensor::conv_data_backward(
213 output_grad,
214 weight,
215 x.shape(),
216 options,
217 Default::default(),
218 )
219 .unwrap()
220 }
221
222 fn conv3d_weight_backward(
223 x: FloatTensor<Self>,
224 weight: FloatTensor<Self>,
225 output_grad: FloatTensor<Self>,
226 options: ConvOptions<3>,
227 ) -> FloatTensor<Self> {
228 rudnn::convolution::tensor::conv_weight_backward::<R, 3>(
229 x,
230 output_grad,
231 weight.shape(),
232 options,
233 Default::default(),
234 )
235 .unwrap()
236 }
237
238 fn conv_transpose2d(
239 x: FloatTensor<Self>,
240 weight: FloatTensor<Self>,
241 bias: Option<FloatTensor<Self>>,
242 options: ConvTransposeOptions<2>,
243 ) -> FloatTensor<Self> {
244 rudnn::convolution::tensor::conv_transpose2d(x, weight, bias, options, ConvTranspose2dStrategy::default())
245 .unwrap()
246 }
247
248 fn conv_transpose3d(
249 x: FloatTensor<Self>,
250 weight: FloatTensor<Self>,
251 bias: Option<FloatTensor<Self>>,
252 options: ConvTransposeOptions<3>,
253 ) -> FloatTensor<Self> {
254 rudnn::convolution::tensor::conv_transpose3d(x, weight, bias, options).expect("Kernel to never fail")
255 }
256
257 fn avg_pool2d(
258 x: FloatTensor<Self>,
259 kernel_size: [usize; 2],
260 stride: [usize; 2],
261 padding: [usize; 2],
262 count_include_pad: bool,
263 ceil_mode: bool,
264 ) -> FloatTensor<Self> {
265 rudnn::pooling::avg_pool2d(
266 x,
267 kernel_size,
268 stride,
269 padding,
270 count_include_pad,
271 ceil_mode,
272 )
273 }
274
275 fn avg_pool2d_backward(
276 x: FloatTensor<Self>,
277 grad: FloatTensor<Self>,
278 kernel_size: [usize; 2],
279 stride: [usize; 2],
280 padding: [usize; 2],
281 count_include_pad: bool,
282 ceil_mode: bool,
283 ) -> FloatTensor<Self> {
284 rudnn::pooling::avg_pool2d_backward(
285 x,
286 grad,
287 kernel_size,
288 stride,
289 padding,
290 count_include_pad,
291 ceil_mode,
292 )
293 }
294
295 fn max_pool2d(
296 x: FloatTensor<Self>,
297 kernel_size: [usize; 2],
298 stride: [usize; 2],
299 padding: [usize; 2],
300 dilation: [usize; 2],
301 ceil_mode: bool,
302 ) -> FloatTensor<Self> {
303 rudnn::pooling::max_pool2d(x, kernel_size, stride, padding, dilation, ceil_mode)
304 }
305
306 fn max_pool2d_with_indices(
307 x: FloatTensor<Self>,
308 kernel_size: [usize; 2],
309 stride: [usize; 2],
310 padding: [usize; 2],
311 dilation: [usize; 2],
312 ceil_mode: bool,
313 ) -> MaxPool2dWithIndices<Self> {
314 let (output, indices) = rudnn::pooling::max_pool2d_with_indices(
315 x,
316 kernel_size,
317 stride,
318 padding,
319 dilation,
320 ceil_mode,
321 I::dtype(),
322 );
323
324 MaxPool2dWithIndices::new(output, indices)
325 }
326
327 fn max_pool2d_with_indices_backward(
328 x: FloatTensor<Self>,
329 kernel_size: [usize; 2],
330 stride: [usize; 2],
331 padding: [usize; 2],
332 dilation: [usize; 2],
333 ceil_mode: bool,
334 output_grad: FloatTensor<Self>,
335 indices: IntTensor<Self>,
336 ) -> MaxPool2dBackward<Self> {
337 MaxPool2dBackward::new(rudnn::pooling::max_pool2d_with_indices_backward(
338 x,
339 output_grad,
340 indices,
341 kernel_size,
342 stride,
343 padding,
344 dilation,
345 ceil_mode,
346 ))
347 }
348
349 fn adaptive_avg_pool2d(x: FloatTensor<Self>, output_size: [usize; 2]) -> FloatTensor<Self> {
350 rudnn::pooling::adaptive_avg_pool2d(x, output_size)
351 }
352
353 fn adaptive_avg_pool3d(x: FloatTensor<Self>, output_size: [usize; 3]) -> FloatTensor<Self> {
354 rudnn::pooling::adaptive_avg_pool3d(x, output_size)
355 }
356
357 fn avg_pool3d(x: FloatTensor<Self>, kernel: [usize; 3], stride: [usize; 3],
358 padding: [usize; 3], include_pad: bool, ceil: bool) -> FloatTensor<Self> {
359 rudnn::pooling::avg_pool3d(x, kernel, stride, padding, include_pad, ceil)
360 }
361
362 fn avg_pool3d_backward(x: FloatTensor<Self>, grad: FloatTensor<Self>, kernel: [usize; 3],
363 stride: [usize; 3], padding: [usize; 3], include_pad: bool, ceil: bool) -> FloatTensor<Self> {
364 rudnn::pooling::avg_pool3d_backward(x, grad, kernel, stride, padding, include_pad, ceil)
365 }
366
367 fn adaptive_avg_pool3d_backward(x: FloatTensor<Self>, grad: FloatTensor<Self>) -> FloatTensor<Self> {
368 rudnn::pooling::adaptive_avg_pool3d_backward(x, grad)
369 }
370
371 fn adaptive_avg_pool2d_backward(
372 x: FloatTensor<Self>,
373 grad: FloatTensor<Self>,
374 ) -> FloatTensor<Self> {
375 rudnn::pooling::adaptive_avg_pool2d_backward(x, grad)
376 }
377
378 fn interpolate(
379 x: FloatTensor<Self>,
380 output_size: [usize; 2],
381 options: InterpolateOptions,
382 ) -> FloatTensor<Self> {
383 rudnn::interpolation::interpolate(x, output_size, options)
384 }
385
386 fn interpolate_backward(
387 x: FloatTensor<Self>,
388 grad: FloatTensor<Self>,
389 output_size: [usize; 2],
390 options: InterpolateOptions,
391 ) -> FloatTensor<Self> {
392 rudnn::interpolation::interpolate_backward(x, grad, output_size, options)
393 }
394
395 fn attention(
396 query: FloatTensor<Self>,
397 key: FloatTensor<Self>,
398 value: FloatTensor<Self>,
399 mask: Option<BoolTensor<Self>>,
400 attn_bias: Option<FloatTensor<Self>>,
401 options: AttentionModuleOptions,
402 ) -> FloatTensor<Self> {
403 if attn_bias.is_some() || options.softcap.is_some() || options.scale.is_some() {
405 return ruda_tensor::ops::attention::attention_fallback::<Self>(
406 query, key, value, mask, attn_bias, options,
407 );
408 }
409
410 rudnn::attention::tensor::attention(
411 query,
412 key,
413 value,
414 mask,
415 attn_bias,
416 options,
417 Default::default(),
418 )
419 .expect("Kernel to never fail")
420 }
421
422 fn has_ctc_loss_backward() -> bool {
423 true
424 }
425
426 fn ctc_loss(
427 log_probs: FloatTensor<Self>,
428 targets: IntTensor<Self>,
429 input_lengths: IntTensor<Self>,
430 target_lengths: IntTensor<Self>,
431 blank: usize,
432 ) -> FloatTensor<Self> {
433 rudnn::ctc::ctc_loss(log_probs, targets, input_lengths, target_lengths, blank)
434 }
435
436 fn ctc_loss_backward(
437 log_probs: FloatTensor<Self>,
438 targets: IntTensor<Self>,
439 input_lengths: IntTensor<Self>,
440 target_lengths: IntTensor<Self>,
441 grad_loss: FloatTensor<Self>,
442 blank: usize,
443 ) -> FloatTensor<Self> {
444 let (log_alpha_full, log_beta_full, nll) = rudnn::ctc::ctc_alpha_beta(
445 log_probs.clone(),
446 targets.clone(),
447 input_lengths.clone(),
448 target_lengths,
449 blank,
450 );
451 ruda_tensor::ops::ctc::ctc_grad_from_alpha_beta_default::<Self>(
452 log_probs,
453 targets,
454 input_lengths,
455 grad_loss,
456 log_alpha_full,
457 log_beta_full,
458 nll,
459 blank,
460 )
461 }
462
463 fn rfft(
464 signal: FloatTensor<Self>,
465 dim: usize,
466 n: Option<usize>,
467 ) -> (FloatTensor<Self>, FloatTensor<Self>) {
468 rufft::tensor::rfft(signal, dim, n)
469 }
470
471 fn irfft(
472 spectrum_re: FloatTensor<Self>,
473 spectrum_im: FloatTensor<Self>,
474 dim: usize,
475 n: Option<usize>,
476 ) -> FloatTensor<Self> {
477 rufft::tensor::irfft(spectrum_re, spectrum_im, dim, n)
478 }
479}