@@ -656,17 +656,20 @@ def get_inputs(self):
656656 conv_count = 1 ,
657657 )
658658
659- def test_padded_output_tconv (self ):
660- class TConv2d (torch .nn .Module ):
661- def __init__ (self ):
659+ def test_fp32_tconv_output_padding (self ):
660+ """Test transposed convolution with non-zero output padding."""
661+
662+ class TConv2dOutputPadding (torch .nn .Module ):
663+ def __init__ (self , output_padding ):
662664 super ().__init__ ()
665+ self .transpose = True
663666 self .conv = torch .nn .ConvTranspose2d (
664667 in_channels = 2 ,
665668 out_channels = 1 ,
666669 kernel_size = (3 , 3 ),
667670 stride = (2 , 2 ),
668671 padding = (1 , 1 ),
669- output_padding = ( 0 , 1 ) ,
672+ output_padding = output_padding ,
670673 dilation = (1 , 1 ),
671674 groups = 1 ,
672675 bias = True ,
@@ -675,26 +678,181 @@ def __init__(self):
675678 def forward (self , x ):
676679 return self .conv (x )
677680
678- m = TConv2d ()
679- inputs = (torch .randn (1 , 2 , 8 , 8 ),)
680- tester = Tester (m .eval (), inputs )
681+ def get_inputs (self ):
682+ return (torch .randn (1 , 2 , 8 , 8 ),)
681683
682- conv_count : int = 1
683- op = "torch.ops.aten.conv_transpose2d"
684+ # Test asymmetric output padding (0, 1)
685+ self . _test ( TConv2dOutputPadding ( output_padding = ( 0 , 1 )))
684686
685- (tester .export ().check_count ({op : conv_count }).to_edge_transform_and_lower ())
687+ # Test symmetric output padding (1, 1)
688+ self ._test (TConv2dOutputPadding (output_padding = (1 , 1 )))
686689
687- # tconv should not be offloaded to XNNPack, since output padding is not supported
688- (
689- tester .check (
690- ["executorch_exir_dialects_edge__ops_aten_convolution_default" ]
690+ def test_qs8_tconv_output_padding (self ):
691+ """Test quantized transposed convolution with non-zero output padding."""
692+
693+ class TConv2dOutputPadding (torch .nn .Module ):
694+ def __init__ (self , output_padding ):
695+ super ().__init__ ()
696+ self .transpose = True
697+ self .conv = torch .nn .ConvTranspose2d (
698+ in_channels = 2 ,
699+ out_channels = 1 ,
700+ kernel_size = (3 , 3 ),
701+ stride = (2 , 2 ),
702+ padding = (1 , 1 ),
703+ output_padding = output_padding ,
704+ dilation = (1 , 1 ),
705+ groups = 1 ,
706+ bias = True ,
707+ ).to (torch .float )
708+
709+ def forward (self , x ):
710+ return self .conv (x )
711+
712+ def get_inputs (self ):
713+ return (torch .randn (1 , 2 , 8 , 8 ),)
714+
715+ # Test asymmetric output padding (0, 1) with quantization
716+ self ._test (
717+ TConv2dOutputPadding (output_padding = (0 , 1 )),
718+ quant_config = get_symmetric_quantization_config (),
719+ )
720+
721+ # Test symmetric output padding (1, 1) with quantization
722+ self ._test (
723+ TConv2dOutputPadding (output_padding = (1 , 1 )),
724+ quant_config = get_symmetric_quantization_config (),
725+ )
726+
727+ def test_fp32_tconv_output_padding_large_stride (self ):
728+ """Test transposed convolution with larger output padding and stride values."""
729+
730+ class TConv2dLargeOutputPadding (torch .nn .Module ):
731+ def __init__ (self , stride , output_padding ):
732+ super ().__init__ ()
733+ self .transpose = True
734+ self .conv = torch .nn .ConvTranspose2d (
735+ in_channels = 8 ,
736+ out_channels = 16 ,
737+ kernel_size = (5 , 5 ),
738+ stride = stride ,
739+ padding = (2 , 2 ),
740+ output_padding = output_padding ,
741+ dilation = (1 , 1 ),
742+ groups = 1 ,
743+ bias = True ,
744+ ).to (torch .float )
745+
746+ def forward (self , x ):
747+ return self .conv (x )
748+
749+ def get_inputs (self ):
750+ return (torch .randn (2 , 8 , 16 , 16 ),)
751+
752+ # Test with stride=4 and output_padding=(3, 3) - maximum valid for stride 4
753+ self ._test (TConv2dLargeOutputPadding (stride = (4 , 4 ), output_padding = (3 , 3 )))
754+
755+ # Test with stride=3 and asymmetric output_padding=(2, 1)
756+ self ._test (TConv2dLargeOutputPadding (stride = (3 , 3 ), output_padding = (2 , 1 )))
757+
758+ # Test with asymmetric stride and output_padding
759+ self ._test (TConv2dLargeOutputPadding (stride = (4 , 3 ), output_padding = (3 , 2 )))
760+
761+ def test_fp32_tconv_output_padding_various_shapes (self ):
762+ """Test transposed convolution with output padding on various input shapes."""
763+
764+ class TConv2dVariousShapes (torch .nn .Module ):
765+ def __init__ (
766+ self ,
767+ in_channels ,
768+ out_channels ,
769+ kernel_size ,
770+ stride ,
771+ padding ,
772+ output_padding ,
773+ height ,
774+ width ,
775+ ):
776+ super ().__init__ ()
777+ self .transpose = True
778+ self .height = height
779+ self .width = width
780+ self .in_channels = in_channels
781+ self .conv = torch .nn .ConvTranspose2d (
782+ in_channels = in_channels ,
783+ out_channels = out_channels ,
784+ kernel_size = kernel_size ,
785+ stride = stride ,
786+ padding = padding ,
787+ output_padding = output_padding ,
788+ dilation = (1 , 1 ),
789+ groups = 1 ,
790+ bias = True ,
791+ ).to (torch .float )
792+
793+ def forward (self , x ):
794+ return self .conv (x )
795+
796+ def get_inputs (self ):
797+ return (torch .randn (1 , self .in_channels , self .height , self .width ),)
798+
799+ # Test with larger kernel (7x7), stride=2, output_padding=1
800+ self ._test (
801+ TConv2dVariousShapes (
802+ in_channels = 3 ,
803+ out_channels = 32 ,
804+ kernel_size = (7 , 7 ),
805+ stride = (2 , 2 ),
806+ padding = (3 , 3 ),
807+ output_padding = (1 , 1 ),
808+ height = 32 ,
809+ width = 32 ,
691810 )
692- .check_not (["torch.ops.higher_order.executorch_call_delegate" ])
693- .to_executorch ()
694- .serialize ()
695- .run_method_and_compare_outputs (qtol = 1 )
696811 )
697812
813+ # Test with rectangular kernel and asymmetric output padding
814+ self ._test (
815+ TConv2dVariousShapes (
816+ in_channels = 16 ,
817+ out_channels = 8 ,
818+ kernel_size = (3 , 5 ),
819+ stride = (2 , 3 ),
820+ padding = (1 , 2 ),
821+ output_padding = (1 , 2 ),
822+ height = 24 ,
823+ width = 32 ,
824+ )
825+ )
826+
827+ # Test with small spatial dimensions but larger output padding
828+ self ._test (
829+ TConv2dVariousShapes (
830+ in_channels = 4 ,
831+ out_channels = 4 ,
832+ kernel_size = (4 , 4 ),
833+ stride = (4 , 4 ),
834+ padding = (0 , 0 ),
835+ output_padding = (3 , 3 ),
836+ height = 4 ,
837+ width = 4 ,
838+ )
839+ )
840+
841+ # Test with batch size > 1 and asymmetric everything
842+ model = TConv2dVariousShapes (
843+ in_channels = 6 ,
844+ out_channels = 12 ,
845+ kernel_size = (5 , 3 ),
846+ stride = (3 , 2 ),
847+ padding = (2 , 1 ),
848+ output_padding = (2 , 1 ),
849+ height = 20 ,
850+ width = 15 ,
851+ )
852+ # Override get_inputs to use larger batch
853+ model .get_inputs = lambda : (torch .randn (4 , 6 , 20 , 15 ),)
854+ self ._test (model )
855+
698856 def test_dq_conv2d (self ) -> None :
699857 model = Conv2d (
700858 in_channels = 3 ,
0 commit comments