1use super::{conv, ctc, embedding, linear, pool};
2use crate::ops::unfold::unfold4d_using_conv2d;
3use crate::tensor::{BoolTensor, FloatTensor, IntTensor};
4use crate::{Backend, ElementConversion, TensorMetadata};
5use ruda_core::tensor::Shape;
6
7pub struct LayerNormOutput<B: Backend> {
9 pub output: FloatTensor<B>,
11 pub mean: FloatTensor<B>,
13 pub rstd: FloatTensor<B>,
15}
16
17pub struct LayerNormBackward<B: Backend> {
19 pub input: FloatTensor<B>,
21 pub weight: FloatTensor<B>,
23 pub bias: FloatTensor<B>,
25}
26
27#[derive(new)]
29pub struct Conv2dBackward<B: Backend> {
30 pub x_grad: FloatTensor<B>,
32
33 pub weights_grad: FloatTensor<B>,
35
36 pub bias_grad: Option<FloatTensor<B>>,
38}
39
40#[derive(new)]
42pub struct DeformConv2dBackward<B: Backend> {
43 pub x_grad: FloatTensor<B>,
45
46 pub offset_grad: FloatTensor<B>,
48
49 pub weight_grad: FloatTensor<B>,
51
52 pub mask_grad: Option<FloatTensor<B>>,
54
55 pub bias_grad: Option<FloatTensor<B>>,
57}
58
59#[derive(new)]
61pub struct Conv3dBackward<B: Backend> {
62 pub x_grad: FloatTensor<B>,
64
65 pub weights_grad: FloatTensor<B>,
67
68 pub bias_grad: Option<FloatTensor<B>>,
70}
71
72#[derive(new)]
74pub struct MaxPool1dBackward<B: Backend> {
75 pub x_grad: FloatTensor<B>,
77}
78
79#[derive(new)]
81pub struct MaxPool1dWithIndices<B: Backend> {
82 pub output: FloatTensor<B>,
84
85 pub indices: IntTensor<B>,
87}
88
89#[derive(new)]
91pub struct MaxPool2dBackward<B: Backend> {
92 pub x_grad: FloatTensor<B>,
94}
95
96#[derive(new)]
98pub struct MaxPool2dWithIndices<B: Backend> {
99 pub output: FloatTensor<B>,
101
102 pub indices: IntTensor<B>,
104}
105
106pub use ruda_core::tensor::spatial::{ConvOptions, PaddedConvOptions, DeformConvOptions, ConvTransposeOptions, UnfoldOptions};
107
108pub use ruda_core::tensor::spatial::{InterpolateMode, InterpolateOptions};
109
110pub use ruda_core::tensor::spatial::{GridSampleOptions, GridSamplePaddingMode};
111
112#[derive(Debug, Clone, Copy, PartialEq, serde::Deserialize, serde::Serialize)]
123pub enum PadMode {
124 Constant(f32),
130
131 Reflect,
139
140 Edge,
146}
147
148impl Default for PadMode {
149 fn default() -> Self {
150 PadMode::Constant(0.0)
151 }
152}
153
154impl<E: ElementConversion> From<E> for PadMode {
155 fn from(value: E) -> Self {
156 PadMode::Constant(value.elem())
157 }
158}
159
160#[derive(new)]
162pub struct InterpolateBackward<B: Backend> {
163 pub x_grad: FloatTensor<B>,
165}
166
167pub use ruda_core::tensor::spatial::AttentionModuleOptions;
168
169pub trait ModuleOps<B: Backend> {
171 fn embedding(weights: FloatTensor<B>, indices: IntTensor<B>) -> FloatTensor<B> {
182 embedding::embedding::<B>(weights, indices)
183 }
184
185 fn embedding_backward(
197 weights: FloatTensor<B>,
198 output_grad: FloatTensor<B>,
199 indices: IntTensor<B>,
200 ) -> FloatTensor<B> {
201 embedding::embedding_backward::<B>(weights, output_grad, indices)
202 }
203
204 fn linear(
212 x: FloatTensor<B>,
213 weight: FloatTensor<B>,
214 bias: Option<FloatTensor<B>>,
215 ) -> FloatTensor<B> {
216 linear::linear::<B>(x, weight, bias)
217 }
218 fn linear_x_backward(weight: FloatTensor<B>, output_grad: FloatTensor<B>) -> FloatTensor<B> {
220 linear::linear_x_backward::<B>(weight, output_grad)
221 }
222 fn linear_weight_backward(x: FloatTensor<B>, output_grad: FloatTensor<B>) -> FloatTensor<B> {
224 linear::linear_weight_backward::<B>(x, output_grad)
225 }
226 fn linear_bias_backward(output_grad: FloatTensor<B>) -> FloatTensor<B> {
228 linear::linear_bias_backward::<B>(output_grad)
229 }
230
231 fn conv1d(
239 x: FloatTensor<B>,
240 weight: FloatTensor<B>,
241 bias: Option<FloatTensor<B>>,
242 options: ConvOptions<1>,
243 ) -> FloatTensor<B> {
244 conv::conv1d_from_conv2d::<B>(x, weight, bias, options)
245 }
246 fn conv1d_x_backward(
248 x: FloatTensor<B>,
249 weight: FloatTensor<B>,
250 output_grad: FloatTensor<B>,
251 options: ConvOptions<1>,
252 ) -> FloatTensor<B> {
253 conv::conv1d_x_backward::<B>(x, weight, output_grad, options)
254 }
255 fn conv1d_weight_backward(
257 x: FloatTensor<B>,
258 weight: FloatTensor<B>,
259 output_grad: FloatTensor<B>,
260 options: ConvOptions<1>,
261 ) -> FloatTensor<B> {
262 conv::conv1d_weight_backward::<B>(x, weight, output_grad, options)
263 }
264 fn conv1d_bias_backward(
266 x: FloatTensor<B>,
267 bias: FloatTensor<B>,
268 output_grad: FloatTensor<B>,
269 ) -> FloatTensor<B> {
270 conv::conv1d_bias_backward::<B>(x, bias, output_grad)
271 }
272 fn conv2d(
280 x: FloatTensor<B>,
281 weight: FloatTensor<B>,
282 bias: Option<FloatTensor<B>>,
283 options: ConvOptions<2>,
284 ) -> FloatTensor<B>;
285 fn conv2d_x_backward(
287 x: FloatTensor<B>,
288 weight: FloatTensor<B>,
289 output_grad: FloatTensor<B>,
290 options: ConvOptions<2>,
291 ) -> FloatTensor<B> {
292 conv::conv2d_x_backward::<B>(x, weight, output_grad, options)
293 }
294 fn conv2d_weight_backward(
296 x: FloatTensor<B>,
297 weight: FloatTensor<B>,
298 output_grad: FloatTensor<B>,
299 options: ConvOptions<2>,
300 ) -> FloatTensor<B> {
301 conv::conv2d_weight_backward::<B>(x, weight, output_grad, options)
302 }
303 fn conv2d_bias_backward(
305 x: FloatTensor<B>,
306 bias: FloatTensor<B>,
307 output_grad: FloatTensor<B>,
308 ) -> FloatTensor<B> {
309 conv::conv2d_bias_backward::<B>(x, bias, output_grad)
310 }
311
312 fn deform_conv2d(
320 x: FloatTensor<B>,
321 offset: FloatTensor<B>,
322 weight: FloatTensor<B>,
323 mask: Option<FloatTensor<B>>,
324 bias: Option<FloatTensor<B>>,
325 options: DeformConvOptions<2>,
326 ) -> FloatTensor<B>;
327 fn deform_conv2d_backward(
329 x: FloatTensor<B>,
330 offset: FloatTensor<B>,
331 weight: FloatTensor<B>,
332 mask: Option<FloatTensor<B>>,
333 bias: Option<FloatTensor<B>>,
334 output_grad: FloatTensor<B>,
335 options: DeformConvOptions<2>,
336 ) -> DeformConv2dBackward<B>;
337
338 fn conv3d(
346 x: FloatTensor<B>,
347 weight: FloatTensor<B>,
348 bias: Option<FloatTensor<B>>,
349 options: ConvOptions<3>,
350 ) -> FloatTensor<B>;
351 fn conv3d_x_backward(
353 x: FloatTensor<B>,
354 weight: FloatTensor<B>,
355 output_grad: FloatTensor<B>,
356 options: ConvOptions<3>,
357 ) -> FloatTensor<B> {
358 conv::conv3d_x_backward::<B>(x, weight, output_grad, options)
359 }
360 fn conv3d_weight_backward(
362 x: FloatTensor<B>,
363 weight: FloatTensor<B>,
364 output_grad: FloatTensor<B>,
365 options: ConvOptions<3>,
366 ) -> FloatTensor<B> {
367 conv::conv3d_weight_backward::<B>(x, weight, output_grad, options)
368 }
369 fn conv3d_bias_backward(
371 x: FloatTensor<B>,
372 bias: FloatTensor<B>,
373 output_grad: FloatTensor<B>,
374 ) -> FloatTensor<B> {
375 conv::conv3d_bias_backward::<B>(x, bias, output_grad)
376 }
377 fn conv_transpose1d(
385 x: FloatTensor<B>,
386 weight: FloatTensor<B>,
387 bias: Option<FloatTensor<B>>,
388 options: ConvTransposeOptions<1>,
389 ) -> FloatTensor<B> {
390 conv::conv_transpose1d_from_conv_transpose2d::<B>(x, weight, bias, options)
391 }
392 fn conv_transpose1d_x_backward(
394 weight: FloatTensor<B>,
395 output_grad: FloatTensor<B>,
396 options: ConvTransposeOptions<1>,
397 ) -> FloatTensor<B> {
398 conv::conv_transpose1d_x_backward::<B>(weight, output_grad, options)
399 }
400 fn conv_transpose1d_weight_backward(
402 x: FloatTensor<B>,
403 weight: FloatTensor<B>,
404 output_grad: FloatTensor<B>,
405 options: ConvTransposeOptions<1>,
406 ) -> FloatTensor<B> {
407 conv::conv_transpose1d_weight_backward::<B>(x, weight, output_grad, options)
408 }
409 fn conv_transpose1d_bias_backward(
411 x: FloatTensor<B>,
412 bias: FloatTensor<B>,
413 output_grad: FloatTensor<B>,
414 ) -> FloatTensor<B> {
415 conv::conv_transpose1d_bias_backward::<B>(x, bias, output_grad)
416 }
417
418 fn conv_transpose2d(
426 x: FloatTensor<B>,
427 weight: FloatTensor<B>,
428 bias: Option<FloatTensor<B>>,
429 options: ConvTransposeOptions<2>,
430 ) -> FloatTensor<B>;
431 fn conv_transpose2d_x_backward(
433 weight: FloatTensor<B>,
434 output_grad: FloatTensor<B>,
435 options: ConvTransposeOptions<2>,
436 ) -> FloatTensor<B> {
437 conv::conv_transpose2d_x_backward::<B>(weight, output_grad, options)
438 }
439 fn conv_transpose2d_weight_backward(
441 x: FloatTensor<B>,
442 weight: FloatTensor<B>,
443 output_grad: FloatTensor<B>,
444 options: ConvTransposeOptions<2>,
445 ) -> FloatTensor<B> {
446 conv::conv_transpose2d_weight_backward::<B>(x, weight, output_grad, options)
447 }
448 fn conv_transpose2d_bias_backward(
450 x: FloatTensor<B>,
451 bias: FloatTensor<B>,
452 output_grad: FloatTensor<B>,
453 ) -> FloatTensor<B> {
454 conv::conv_transpose2d_bias_backward::<B>(x, bias, output_grad)
455 }
456
457 fn conv_transpose3d(
465 x: FloatTensor<B>,
466 weight: FloatTensor<B>,
467 bias: Option<FloatTensor<B>>,
468 options: ConvTransposeOptions<3>,
469 ) -> FloatTensor<B>;
470 fn conv_transpose3d_x_backward(
472 weight: FloatTensor<B>,
473 output_grad: FloatTensor<B>,
474 options: ConvTransposeOptions<3>,
475 ) -> FloatTensor<B> {
476 conv::conv_transpose3d_x_backward::<B>(weight, output_grad, options)
477 }
478 fn conv_transpose3d_weight_backward(
480 x: FloatTensor<B>,
481 weight: FloatTensor<B>,
482 output_grad: FloatTensor<B>,
483 options: ConvTransposeOptions<3>,
484 ) -> FloatTensor<B> {
485 conv::conv_transpose3d_weight_backward::<B>(x, weight, output_grad, options)
486 }
487 fn conv_transpose3d_bias_backward(
489 x: FloatTensor<B>,
490 bias: FloatTensor<B>,
491 output_grad: FloatTensor<B>,
492 ) -> FloatTensor<B> {
493 conv::conv_transpose3d_bias_backward::<B>(x, bias, output_grad)
494 }
495
496 fn unfold4d(
503 x: FloatTensor<B>,
504 kernel_size: [usize; 2],
505 options: UnfoldOptions,
506 ) -> FloatTensor<B> {
507 if options.padding == [0, 0] && options.dilation == [1, 1] {
508 let blocks = B::float_unfold(x, 2, kernel_size[0], options.stride[0]);
509 let blocks = B::float_unfold(blocks, 3, kernel_size[1], options.stride[1]);
510
511 let blocks = B::float_permute(blocks, &[0, 1, 4, 5, 2, 3]);
514 let shape = blocks.shape();
515
516 B::float_reshape(
519 blocks,
520 [
521 shape[0],
522 shape[1] * shape[2] * shape[3],
523 shape[4] * shape[5],
524 ]
525 .into(),
526 )
527 } else {
528 unfold4d_using_conv2d::<B>(x, kernel_size, options)
529 }
530 }
531
532 fn avg_pool1d(
538 x: FloatTensor<B>,
539 kernel_size: usize,
540 stride: usize,
541 padding: usize,
542 count_include_pad: bool,
543 ceil_mode: bool,
544 ) -> FloatTensor<B> {
545 pool::avg_pool1d_from_2d::<B>(
546 x,
547 kernel_size,
548 stride,
549 padding,
550 count_include_pad,
551 ceil_mode,
552 )
553 }
554 fn avg_pool1d_backward(
556 x: FloatTensor<B>,
557 grad: FloatTensor<B>,
558 kernel_size: usize,
559 stride: usize,
560 padding: usize,
561 count_include_pad: bool,
562 ceil_mode: bool,
563 ) -> FloatTensor<B> {
564 pool::avg_pool1d_backward_from_2d::<B>(
565 x,
566 grad,
567 kernel_size,
568 stride,
569 padding,
570 count_include_pad,
571 ceil_mode,
572 )
573 }
574 fn avg_pool2d(
580 x: FloatTensor<B>,
581 kernel_size: [usize; 2],
582 stride: [usize; 2],
583 padding: [usize; 2],
584 count_include_pad: bool,
585 ceil_mode: bool,
586 ) -> FloatTensor<B>;
587 fn avg_pool2d_backward(
589 x: FloatTensor<B>,
590 grad: FloatTensor<B>,
591 kernel_size: [usize; 2],
592 stride: [usize; 2],
593 padding: [usize; 2],
594 count_include_pad: bool,
595 ceil_mode: bool,
596 ) -> FloatTensor<B>;
597 fn avg_pool3d(x: FloatTensor<B>, kernel_size: [usize; 3], stride: [usize; 3],
599 padding: [usize; 3], count_include_pad: bool, ceil_mode: bool) -> FloatTensor<B> {
600 pool::avg_pool3d_from_2d::<B>(x, kernel_size, stride, padding, count_include_pad, ceil_mode)
601 }
602 fn avg_pool3d_backward(x: FloatTensor<B>, grad: FloatTensor<B>, kernel_size: [usize; 3],
604 stride: [usize; 3], padding: [usize; 3], count_include_pad: bool, ceil_mode: bool) -> FloatTensor<B> {
605 pool::avg_pool3d_backward_from_2d::<B>(x, grad, kernel_size, stride, padding, count_include_pad, ceil_mode)
606 }
607 fn adaptive_avg_pool2d(x: FloatTensor<B>, output_size: [usize; 2]) -> FloatTensor<B>;
613 fn adaptive_avg_pool2d_backward(x: FloatTensor<B>, grad: FloatTensor<B>) -> FloatTensor<B>;
615 fn adaptive_avg_pool3d(x: FloatTensor<B>, output_size: [usize; 3]) -> FloatTensor<B> {
617 pool::adaptive_avg_pool3d_from_2d::<B>(x, output_size)
618 }
619 fn adaptive_avg_pool3d_backward(x: FloatTensor<B>, grad: FloatTensor<B>) -> FloatTensor<B> {
621 pool::adaptive_avg_pool3d_backward_from_2d::<B>(x, grad)
622 }
623 fn adaptive_avg_pool1d(x: FloatTensor<B>, output_size: usize) -> FloatTensor<B> {
629 pool::adaptive_avg_pool1d_from_2d::<B>(x, output_size)
630 }
631 fn adaptive_avg_pool1d_backward(x: FloatTensor<B>, grad: FloatTensor<B>) -> FloatTensor<B> {
633 pool::adaptive_avg_pool1d_backward_from_2d::<B>(x, grad)
634 }
635 fn max_pool1d(
641 x: FloatTensor<B>,
642 kernel_size: usize,
643 stride: usize,
644 padding: usize,
645 dilation: usize,
646 ceil_mode: bool,
647 ) -> FloatTensor<B> {
648 pool::max_pool1d_from_2d::<B>(x, kernel_size, stride, padding, dilation, ceil_mode)
649 }
650
651 fn max_pool1d_with_indices(
657 x: FloatTensor<B>,
658 kernel_size: usize,
659 stride: usize,
660 padding: usize,
661 dilation: usize,
662 ceil_mode: bool,
663 ) -> MaxPool1dWithIndices<B> {
664 pool::max_pool1d_with_indices_from_2d::<B>(
665 x,
666 kernel_size,
667 stride,
668 padding,
669 dilation,
670 ceil_mode,
671 )
672 }
673 #[allow(clippy::too_many_arguments)]
675 fn max_pool1d_with_indices_backward(
676 x: FloatTensor<B>,
677 kernel_size: usize,
678 stride: usize,
679 padding: usize,
680 dilation: usize,
681 ceil_mode: bool,
682 output_grad: FloatTensor<B>,
683 indices: IntTensor<B>,
684 ) -> MaxPool1dBackward<B> {
685 pool::max_pool1d_with_indices_backward_from_2d::<B>(
686 x,
687 kernel_size,
688 stride,
689 padding,
690 dilation,
691 ceil_mode,
692 output_grad,
693 indices,
694 )
695 }
696
697 fn max_pool2d(
703 x: FloatTensor<B>,
704 kernel_size: [usize; 2],
705 stride: [usize; 2],
706 padding: [usize; 2],
707 dilation: [usize; 2],
708 ceil_mode: bool,
709 ) -> FloatTensor<B>;
710
711 fn max_pool2d_with_indices(
717 x: FloatTensor<B>,
718 kernel_size: [usize; 2],
719 stride: [usize; 2],
720 padding: [usize; 2],
721 dilation: [usize; 2],
722 ceil_mode: bool,
723 ) -> MaxPool2dWithIndices<B>;
724 #[allow(clippy::too_many_arguments)]
726 fn max_pool2d_with_indices_backward(
727 x: FloatTensor<B>,
728 kernel_size: [usize; 2],
729 stride: [usize; 2],
730 padding: [usize; 2],
731 dilation: [usize; 2],
732 ceil_mode: bool,
733 output_grad: FloatTensor<B>,
734 indices: IntTensor<B>,
735 ) -> MaxPool2dBackward<B>;
736
737 fn interpolate(
743 x: FloatTensor<B>,
744 output_size: [usize; 2],
745 options: InterpolateOptions,
746 ) -> FloatTensor<B>;
747
748 fn interpolate_backward(
750 x: FloatTensor<B>,
751 grad: FloatTensor<B>,
752 output_size: [usize; 2],
753 options: InterpolateOptions,
754 ) -> FloatTensor<B>;
755
756 fn attention(
778 query: FloatTensor<B>,
779 key: FloatTensor<B>,
780 value: FloatTensor<B>,
781 mask: Option<BoolTensor<B>>,
782 attn_bias: Option<FloatTensor<B>>,
783 options: AttentionModuleOptions,
784 ) -> FloatTensor<B>;
785
786 fn layer_norm(
802 tensor: FloatTensor<B>,
803 gamma: FloatTensor<B>,
804 beta: Option<FloatTensor<B>>,
805 epsilon: f64,
806 ) -> FloatTensor<B> {
807 Self::layer_norm_default(tensor, gamma, beta, epsilon)
808 }
809
810 fn has_layer_norm_backward() -> bool { false }
812
813 fn layer_norm_with_stats(
815 _tensor: FloatTensor<B>, _gamma: FloatTensor<B>,
816 _beta: Option<FloatTensor<B>>, _epsilon: f64,
817 ) -> LayerNormOutput<B> {
818 unimplemented!("native LayerNorm statistics unavailable")
819 }
820
821 fn layer_norm_backward(
823 _tensor: FloatTensor<B>, _gamma: FloatTensor<B>, _grad: FloatTensor<B>,
824 _mean: FloatTensor<B>, _rstd: FloatTensor<B>,
825 ) -> LayerNormBackward<B> {
826 unimplemented!("native LayerNorm backward unavailable")
827 }
828
829 fn layer_norm_default(
831 tensor: FloatTensor<B>, gamma: FloatTensor<B>,
832 beta: Option<FloatTensor<B>>, epsilon: f64,
833 ) -> FloatTensor<B> {
834 let shape = tensor.shape();
835 let rank = shape.num_dims();
836 let last_dim = rank - 1;
837 let d_model = shape[last_dim];
838
839 let mean = B::float_mean_dim(tensor.clone(), last_dim);
840 let centered = B::float_sub(tensor, mean);
841 let var = B::float_mean_dim(B::float_mul(centered.clone(), centered.clone()), last_dim);
842 let denom = B::float_sqrt(B::float_add_scalar(var, epsilon.into()));
843 let normalized = B::float_div(centered, denom);
844
845 let broadcast_dims: alloc::vec::Vec<usize> = (0..rank)
846 .map(|i| if i == last_dim { d_model } else { 1 })
847 .collect();
848 let gamma_b = B::float_reshape(gamma, Shape::from(broadcast_dims.clone()));
849 let scaled = B::float_mul(normalized, gamma_b);
850
851 match beta {
852 Some(beta) => {
853 let beta_b = B::float_reshape(beta, Shape::from(broadcast_dims));
854 B::float_add(scaled, beta_b)
855 }
856 None => scaled,
857 }
858 }
859
860 fn ctc_loss(
877 log_probs: FloatTensor<B>,
878 targets: IntTensor<B>,
879 input_lengths: IntTensor<B>,
880 target_lengths: IntTensor<B>,
881 blank: usize,
882 ) -> FloatTensor<B> {
883 ctc::ctc_loss_default::<B>(log_probs, targets, input_lengths, target_lengths, blank)
884 }
885
886 fn has_ctc_loss_backward() -> bool {
898 false
899 }
900
901 fn ctc_loss_backward(
921 _log_probs: FloatTensor<B>,
922 _targets: IntTensor<B>,
923 _input_lengths: IntTensor<B>,
924 _target_lengths: IntTensor<B>,
925 _grad_loss: FloatTensor<B>,
926 _blank: usize,
927 ) -> FloatTensor<B> {
928 unreachable!(
929 "ctc_loss_backward called on a backend whose has_ctc_loss_backward() returns false"
930 )
931 }
932
933 fn rfft(
945 signal: FloatTensor<B>,
946 dim: usize,
947 n: Option<usize>,
948 ) -> (FloatTensor<B>, FloatTensor<B>);
949
950 fn irfft(
958 spectrum_re: FloatTensor<B>,
959 spectrum_im: FloatTensor<B>,
960 dim: usize,
961 n: Option<usize>,
962 ) -> FloatTensor<B>;
963}
964
965#[cfg(test)]
966mod tests {
967 use super::*;
968
969 #[test]
970 #[should_panic = "stride must be non-zero"]
971 fn conv_options_stride_zero() {
972 let _opt = ConvOptions::new([0, 1], [0, 0], [1, 1], 1);
973 }
974
975 #[test]
976 #[should_panic = "dilation must be non-zero"]
977 fn conv_options_dilation_zero() {
978 let _opt = ConvOptions::new([1, 1], [0, 0], [0, 0], 1);
979 }
980
981 #[test]
982 #[should_panic = "groups must be non-zero"]
983 fn conv_options_groups_zero() {
984 let _opt = ConvOptions::new([1, 1], [0, 0], [1, 1], 0);
985 }
986
987 #[test]
988 #[should_panic = "stride must be non-zero"]
989 fn conv_transpose_options_stride_zero() {
990 let _opt = ConvTransposeOptions::new([0, 1], [0, 0], [0, 0], [1, 1], 1);
991 }
992
993 #[test]
994 #[should_panic = "dilation must be non-zero"]
995 fn conv_transpose_options_dilation_zero() {
996 let _opt = ConvTransposeOptions::new([1, 1], [0, 0], [0, 0], [0, 0], 1);
997 }
998
999 #[test]
1000 #[should_panic = "groups must be non-zero"]
1001 fn conv_transpose_options_groups_zero() {
1002 let _opt = ConvTransposeOptions::new([1, 1], [0, 0], [0, 0], [1, 1], 0);
1003 }
1004
1005 #[test]
1006 #[should_panic = "stride must be non-zero"]
1007 fn deform_conv_options_stride_zero() {
1008 let _opt = DeformConvOptions::new([0, 1], [0, 0], [1, 1], 1, 1);
1009 }
1010
1011 #[test]
1012 #[should_panic = "dilation must be non-zero"]
1013 fn deform_conv_options_dilation_zero() {
1014 let _opt = DeformConvOptions::new([1, 1], [0, 0], [0, 0], 1, 1);
1015 }
1016
1017 #[test]
1018 #[should_panic = "weight groups must be non-zero"]
1019 fn deform_conv_options_weights_groups_zero() {
1020 let _opt = DeformConvOptions::new([1, 1], [0, 0], [1, 1], 0, 1);
1021 }
1022
1023 #[test]
1024 #[should_panic = "offset groups must be non-zero"]
1025 fn deform_conv_options_offset_groups_zero() {
1026 let _opt = DeformConvOptions::new([1, 1], [0, 0], [1, 1], 1, 0);
1027 }
1028
1029 #[test]
1030 #[should_panic = "stride must be non-zero"]
1031 fn unfold_options_stride_zero() {
1032 let _opt = UnfoldOptions::new([0, 1], [0, 0], [1, 1]);
1033 }
1034
1035 #[test]
1036 #[should_panic = "dilation must be non-zero"]
1037 fn unfold_options_dilation_zero() {
1038 let _opt = UnfoldOptions::new([1, 1], [0, 0], [0, 0]);
1039 }
1040}