Skip to content

Commit b38306e

Browse files
committed
fix conv transpose lowering
1 parent bd14e8f commit b38306e

3 files changed

Lines changed: 65 additions & 66 deletions

File tree

coreai_torch/_aten_to_core.py

Lines changed: 19 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -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 = (

tests/ops/test_ops.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5662,6 +5662,46 @@ def forward(self, x: Tensor) -> Tensor:
56625662
await validate_numerical_output(model=model, x=x, dynamic_shapes=dynamic_shapes)
56635663

56645664

5665+
@pytest.mark.parametrize(
5666+
"is_1d, output_padding",
5667+
[
5668+
(True, 0), # radar repro: conv_transpose1d + squeeze, static shapes
5669+
(True, 1), # output_padding > padding (previously wrong output size)
5670+
(False, 0),
5671+
(False, 1),
5672+
],
5673+
)
5674+
async def test_conv_transpose_static_shape_squeeze(
5675+
is_1d: bool, output_padding: int
5676+
) -> None:
5677+
"""A squeeze/reshape after a transposed conv with fully static input must
5678+
keep static shapes end-to-end so ``save_asset`` passes MLIR verification
5679+
(rdar://181169322: the 1D reshape-back used to erase the static shape,
5680+
tripping ``coreai.shrink_dims`` on dynamic dims)."""
5681+
5682+
class Model(nn.Module):
5683+
def __init__(self) -> None:
5684+
super().__init__()
5685+
self.ct = (
5686+
nn.ConvTranspose1d(4, 1, 8, stride=2, output_padding=output_padding)
5687+
if is_1d
5688+
else nn.ConvTranspose2d(
5689+
4, 1, 3, stride=2, output_padding=output_padding
5690+
)
5691+
)
5692+
5693+
def forward(self, x: Tensor) -> Tensor:
5694+
# squeeze the singleton out-channel dim, then reshape — the pattern
5695+
# that previously produced `shrink_dims` on dynamic dims.
5696+
y = self.ct(x).squeeze(1)
5697+
return y.reshape(y.shape[0], -1)
5698+
5699+
x = torch.randn(1, 4, 32) if is_1d else torch.randn(1, 4, 8, 8)
5700+
# validate_numerical_output saves the asset (exercising MLIR verification)
5701+
# and checks numerics against torch eager.
5702+
await validate_numerical_output(model=Model().eval(), x=x)
5703+
5704+
56655705
@pytest.mark.parametrize("dynamic", [False, True])
56665706
@pytest.mark.parametrize(
56675707
"input_shape,split_sizes,dim,dtype",

tests/ops/test_ops_ir.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2614,17 +2614,12 @@ def forward(self, x: Tensor) -> Tensor:
26142614
check_file="""
26152615
// CHECK-LABEL: module {
26162616
// CHECK-NEXT: coreai.graph @main(%[[ARG0:.*]]: tensor<1x3x4x4xf32> {coreai.name = "x"}) -> (tensor<1x8x8x8xf32> {coreai.name = "{{.*}}"}) attributes {__coreai_pure__} {
2617-
// CHECK-NEXT: %[[V0:.*]] = coreai.constant dense<[1, 8, 9, 9]> : tensor<4xsi32>
2618-
// CHECK-NEXT: %[[V1:.*]] = coreai.constant dense<1> : tensor<4xsi32>
2619-
// CHECK-NEXT: %[[V2:.*]] = coreai.constant dense<[0, 0, 1, 1]> : tensor<4xsi32>
2620-
// CHECK-NEXT: %[[V3:.*]] = coreai.constant dense<{{.*}}> : tensor<3x8x3x3xf32>
2621-
// CHECK-NEXT: %[[V4:.*]] = coreai.constant dense<2> : tensor<2xui32>
2622-
// CHECK-NEXT: %[[V5:.*]] = coreai.constant dense<0> : tensor<2xui32>
2623-
// CHECK-NEXT: %[[V6:.*]] = coreai.constant dense<1> : tensor<2xui32>
2624-
// CHECK-NEXT: %[[V7:.*]] = coreai.constant dense<1> : tensor<ui32>
2625-
// CHECK-NEXT: %[[V8:.*]] = coreai.conv_transpose2d %[[ARG0]], %[[V3]], %[[V4]], %[[V5]], %[[V6]], %[[V5]], %[[V7]] : (tensor<1x3x4x4xf32>, tensor<3x8x3x3xf32>, tensor<2xui32>, tensor<2xui32>, tensor<2xui32>, tensor<2xui32>, tensor<ui32>) -> tensor<1x8x9x9xf32>
2626-
// CHECK-NEXT: %[[V9:.*]] = coreai.slice %[[V8]], %[[V2]], %[[V0]], %[[V1]] : (tensor<1x8x9x9xf32>, tensor<4xsi32>, tensor<4xsi32>, tensor<4xsi32>) -> tensor<1x8x8x8xf32>
2627-
// CHECK-NEXT: coreai.output %[[V9]] : tensor<1x8x8x8xf32>
2617+
// CHECK-NEXT: %[[W:.*]] = coreai.constant dense<{{.*}}> : tensor<3x8x3x3xf32>
2618+
// CHECK-NEXT: %[[STRIDE:.*]] = coreai.constant dense<2> : tensor<2xui32>
2619+
// CHECK-NEXT: %[[ONE:.*]] = coreai.constant dense<1> : tensor<2xui32>
2620+
// CHECK-NEXT: %[[GROUPS:.*]] = coreai.constant dense<1> : tensor<ui32>
2621+
// CHECK-NEXT: %[[R:.*]] = coreai.conv_transpose2d %[[ARG0]], %[[W]], %[[STRIDE]], %[[ONE]], %[[ONE]], %[[ONE]], %[[GROUPS]] : (tensor<1x3x4x4xf32>, tensor<3x8x3x3xf32>, tensor<2xui32>, tensor<2xui32>, tensor<2xui32>, tensor<2xui32>, tensor<ui32>) -> tensor<1x8x8x8xf32>
2622+
// CHECK-NEXT: coreai.output %[[R]] : tensor<1x8x8x8xf32>
26282623
// CHECK-NEXT: }
26292624
// CHECK-NEXT: }
26302625
""",

0 commit comments

Comments
 (0)