@@ -1148,7 +1148,8 @@ def _conv_transpose(
11481148 """Handles transposed convolution (conv_transpose1d and conv_transpose2d).
11491149
11501150 For 1D, expands to 2D, performs conv_transpose2d, then shrinks back.
1151- Handles output_padding via pre-padding input and post-cropping output.
1151+ ``padding`` and ``output_padding`` are handled natively by the Core AI
1152+ ``conv_transpose2d`` op (matching PyTorch semantics).
11521153 """
11531154 is_1d = x .type .rank == 3
11541155 if is_1d :
@@ -1160,66 +1161,29 @@ def _conv_transpose(
11601161 dilation = dilation + [1 ]
11611162 output_padding = output_padding + [0 ]
11621163
1163- x_rank = x .type .rank
1164- effective_padding = padding
1165- pre_pad_amt = [0 ] * (x_rank * 2 )
1166- post_crop_amt = [0 ] * (x_rank * 2 )
1167-
1168- if any (p > 0 for p in output_padding ):
1169- effective_padding = [0 ] * len (padding )
1170- pre_pad_amt = [0 ] * (x_rank * 2 )
1171- post_crop_amt = [0 ] * (x_rank * 2 )
1172- # For each spatial dim: initialize symmetric crop from padding,
1173- # then shift the output_padding amount from crop → pre-pad if needed
1174- for i , (p , op ) in enumerate (zip (padding , output_padding )):
1175- before = 4 + 2 * i
1176- after = 4 + 2 * i + 1
1177- post_crop_amt [before ] = p
1178- post_crop_amt [after ] = p
1179- if post_crop_amt [after ] >= op :
1180- post_crop_amt [after ] -= op
1181- else :
1182- pre_pad_amt [after ] = op - post_crop_amt [after ]
1183- post_crop_amt [after ] = 0
1184-
1185- if any (p > 0 for p in pre_pad_amt ):
1186- x = coreai .pad (
1187- x ,
1188- np .array (pre_pad_amt , dtype = np .uint32 ),
1189- coreai .constant (0 , dtype = x .type .element_type ),
1190- )
1191- stride = coreai .constant (stride , np .uint32 )
1192- effective_padding = coreai .constant (effective_padding , np .uint32 )
1193- dilation = coreai .constant (dilation , np .uint32 )
1194- output_padding = coreai .constant ([0 , 0 ], dtype = np .uint32 )
1195- groups = coreai .constant (groups , np .uint32 )
11961164 result = coreai .conv_transpose2d (
11971165 input = x ,
11981166 weight = weight ,
1199- stride = stride ,
1200- padding = effective_padding ,
1201- dilation = dilation ,
1202- output_pad = output_padding ,
1203- groups = groups ,
1167+ stride = coreai . constant ( stride , np . uint32 ) ,
1168+ padding = coreai . constant ( padding , np . uint32 ) ,
1169+ dilation = coreai . constant ( dilation , np . uint32 ) ,
1170+ output_pad = coreai . constant ( output_padding , np . uint32 ) ,
1171+ groups = coreai . constant ( groups , np . uint32 ) ,
12041172 )
12051173
1206- if any (p > 0 for p in post_crop_amt ):
1207- stop_val = coreai .sub (
1208- coreai .cast (coreai .get_shape (result ), dtype = np .int32 ),
1209- [post_crop_amt [2 * d + 1 ] for d in range (x_rank )],
1210- )
1211- result = coreai .slice_ (
1212- result ,
1213- [post_crop_amt [2 * d ] for d in range (x_rank )],
1214- stop_val ,
1215- [1 ] * x_rank ,
1216- )
1217-
12181174 if is_1d :
1219- # Shrink back to 3D: [N,C,W,1] → [N,C,W]
1220- result = coreai .reshape (
1221- result , coreai .slice_ (coreai .get_shape (result ), [0 ], [3 ], [1 ])
1222- )
1175+ # Shrink back to 3D: [N,C,W,1] → [N,C,W]. When the trailing (added) dim
1176+ # is statically 1, use shrink_dims — the inverse of the expand_dims
1177+ # above — which preserves the statically-known output shape so
1178+ # downstream ops (e.g. squeeze) stay static (rdar://181169322). Under a
1179+ # dynamic input the conv op reports every dim (incl. the added one) as
1180+ # dynamic, so fall back to a reshape driven by the runtime shape.
1181+ if result .type .shape [- 1 ] == 1 :
1182+ result = coreai .shrink_dims (result , [- 1 ])
1183+ else :
1184+ result = coreai .reshape (
1185+ result , coreai .slice_ (coreai .get_shape (result ), [0 ], [3 ], [1 ])
1186+ )
12231187
12241188 if bias is not None :
12251189 bias_shape = (
0 commit comments