Get conv_transpose weight in MLX format, handling grouped convolutions. PyTorch conv_transpose weight shape: [C_in, C_out/G, *K] MLX expects: [C_out, *K, C_in/G] For groups=1, a simple permute suffices (C_in==C_in/G, C_out/G==C_out). For groups>1, we need re
(
P: MLXProgramBuilder, w_node: Node, groups: int, ndim: int
)
| 2239 | |
| 2240 | |
| 2241 | def _emit_conv_transpose_weight( |
| 2242 | P: MLXProgramBuilder, w_node: Node, groups: int, ndim: int |
| 2243 | ) -> Slot: |
| 2244 | """Get conv_transpose weight in MLX format, handling grouped convolutions. |
| 2245 | |
| 2246 | PyTorch conv_transpose weight shape: [C_in, C_out/G, *K] |
| 2247 | MLX expects: [C_out, *K, C_in/G] |
| 2248 | |
| 2249 | For groups=1, a simple permute suffices (C_in==C_in/G, C_out/G==C_out). |
| 2250 | For groups>1, we need reshape-permute-reshape to rearrange the group dim: |
| 2251 | [C_in, C_out/G, *K] -> [G, C_in/G, C_out/G, *K] |
| 2252 | -> [G, C_out/G, *K, C_in/G] |
| 2253 | -> [C_out, *K, C_in/G] |
| 2254 | """ |
| 2255 | if groups == 1: |
| 2256 | # Simple permute: [C_in, C_out, *K] -> [C_out, *K, C_in] |
| 2257 | # e.g. 1D: [1, 2, 0], 2D: [1, 2, 3, 0], 3D: [1, 2, 3, 4, 0] |
| 2258 | perm = list(range(1, ndim + 2)) + [0] |
| 2259 | return _emit_channel_last_weight(P, w_node, perm) |
| 2260 | |
| 2261 | # Grouped: need reshape-permute-reshape at compile time |
| 2262 | if w_node.op != "placeholder": |
| 2263 | raise ValueError( |
| 2264 | f"conv_transpose with groups > 1 requires static weights, " |
| 2265 | f"got dynamic weight from {w_node.op}" |
| 2266 | ) |
| 2267 | |
| 2268 | w_target, w_tensor = P.get_placeholder_target_and_tensor(w_node) |
| 2269 | c_in = w_tensor.shape[0] |
| 2270 | c_out_per_g = w_tensor.shape[1] |
| 2271 | kernel_shape = list(w_tensor.shape[2:]) |
| 2272 | c_in_per_g = c_in // groups |
| 2273 | |
| 2274 | # [C_in, C_out/G, *K] -> [G, C_in/G, C_out/G, *K] |
| 2275 | w = w_tensor.reshape([groups, c_in_per_g, c_out_per_g] + kernel_shape) |
| 2276 | # [G, C_in/G, C_out/G, *K] -> [G, C_out/G, *K, C_in/G] |
| 2277 | # perm: [0, 2, 3, ..., ndim+1, 1] |
| 2278 | perm = [0, 2] + list(range(3, ndim + 3)) + [1] |
| 2279 | w = w.permute(perm).contiguous() |
| 2280 | # [G, C_out/G, *K, C_in/G] -> [C_out, *K, C_in/G] |
| 2281 | c_out = groups * c_out_per_g |
| 2282 | w = w.reshape([c_out] + kernel_shape + [c_in_per_g]) |
| 2283 | |
| 2284 | return P.make_or_get_constant(f"{w_target}_channel_last", w) |
| 2285 | |
| 2286 | |
| 2287 | def _emit_conv_bias( |
no test coverage detected