def test_conv1d_channellast_composition(
        self, backend, has_strides, pad_type, has_pad, has_dilations, has_bias
    ):
        """
        Input graph:
        input -> expand_dims -> transpose -> conv2d -> transpose -> squeeze -> out

        Output graph:
        input -> transpose -> conv1d -> transpose -> out
        """
        N, L = 2, 8
        C_in, C_out = 5, 3
        K = 3

        conv_kwargs = {
            "weight": np.random.rand(C_out, C_in, 1, K),
            "pad_type": pad_type,
        }
        if has_strides:
            conv_kwargs["strides"] = (2, 2)
        if has_pad:
            # The pad is specially designed to make sure the output of conv has dim_size=1 at axis 1.
            conv_kwargs["pad"] = (0, 0, 1, 1) if pad_type == "custom" else (1, 1, 1, 1)
        if has_dilations:
            conv_kwargs["dilations"] = (2, 2)
        if has_bias:
            conv_kwargs["bias"] = np.random.rand(C_out)

        @mb.program(input_specs=[mb.TensorSpec(shape=(N, L, C_in))])
        def prog(x):
            y_expand = mb.expand_dims(x=x, axes=(1,))
            y_transpose1 = mb.transpose(x=y_expand, perm=(0, 3, 1, 2))
            y_conv = mb.conv(x=y_transpose1, **conv_kwargs)
            y_transpose2 = mb.transpose(x=y_conv, perm=(0, 2, 3, 1))
            y_squeeze = mb.squeeze(x=y_transpose2, axes=(1,))
            return y_squeeze

        assert get_op_types_in_program(prog) == [
            "expand_dims",
            "transpose",
            "conv",
            "transpose",
            "squeeze",
        ]

        prev_prog, prev_block, block = apply_pass_and_basic_check(prog, "common::compose_conv1d")
        assert get_op_types_in_program(prog) == ["transpose", "squeeze", "conv", "transpose"]

        prev_prog, prev_block, block = apply_pass_and_basic_check(prog, "common::const_elimination")
        assert get_op_types_in_program(prog) == ["transpose", "conv", "transpose"]

        # infer output shape
        strides = conv_kwargs["strides"] if has_strides else (1, 1)
        pad = conv_kwargs["pad"] if has_pad else (0, 0, 0, 0)
        dilations = conv_kwargs["dilations"] if has_dilations else (1, 1)
        L_out = None
        if pad_type == "valid":
            L_out = (L - dilations[-1] * (K - 1) - 1) // strides[-1] + 1
        elif pad_type == "custom":
            L_out = (L + pad[-2] + pad[-1] - dilations[-1] * (K - 1) - 1) // strides[-1] + 1
        elif pad_type == "same":
            L_out = np.ceil(L / strides[-1])
        else:
            raise Exception("unsupported pad type")
        output_shape = (N, L_out, C_out)

        assert_model_is_valid(
            prog,
            {"x": (N, L, C_in)},
            expected_output_shapes={block.outputs[0].name: output_shape},
            backend=backend,
        )

        Input graph:
        input -> expand_dims -> transpose -> conv2d -> transpose -> squeeze -> out

        Output graph:
        input -> transpose -> conv1d -> transpose -> out
        
coremltools/converters/mil/mil/passes/tests/test_passes.py