def test_different_input_ranks_no_mask(
        self, compute_unit, backend, frontend, minimum_deployment_target, rank, dynamic
    ):
        """
        The query/key/value inputs can be any rank 2 or greater.
        """
        if rank in [3, 4, 5]:
            pytest.xfail(
                "rdar://139827570 (ExecuTorch frontend test failures because the MLModel couldn't be loaded)"
            )

        batch_size, seq_len, n_heads_1, n_heads_2, embedding_dim = 2, 10, 3, 4, 7
        if rank == 2:
            input_shape = (seq_len, embedding_dim)
        elif rank == 3:
            input_shape = (batch_size, seq_len, embedding_dim)
        elif rank == 4:
            input_shape = (batch_size, n_heads_1, seq_len, embedding_dim)
        else:
            assert rank == 5
            input_shape = (batch_size, n_heads_1, n_heads_2, seq_len, embedding_dim)

        if frontend in TORCH_EXPORT_BASED_FRONTENDS:

            class Model(nn.Module):
                def forward(self, query, key, value):
                    return nn.functional.scaled_dot_product_attention(query, key, value)

            model = Model()
        else:
            model = ModuleWrapper(function=nn.functional.scaled_dot_product_attention)

        if dynamic:
            upper_bound = 10
            batch_coreml = ct.RangeDim(default=batch_size, upper_bound=upper_bound)
            batch_torch = torch.export.Dim(name="batch", max=upper_bound)
            n_heads_1_coreml = ct.RangeDim(default=n_heads_1, upper_bound=upper_bound)
            n_heads_1_torch = torch.export.Dim(name="n_heads_1", max=upper_bound)
            n_heads_2_coreml = ct.RangeDim(default=n_heads_2, upper_bound=upper_bound)
            n_heads_2_torch = torch.export.Dim(name="n_heads_2", max=upper_bound)
            seq_coreml = ct.RangeDim(default=seq_len, upper_bound=upper_bound)
            seq_torch = torch.export.Dim(name="seq", max=upper_bound)
            if rank == 2:
                converter_input_type = [
                    ct.TensorType(shape=(seq_coreml, embedding_dim)) for _ in range(3)
                ]
                torch_export_dynamic_shapes = {
                    "query": {0: seq_torch},
                    "key": {0: seq_torch},
                    "value": {0: seq_torch},
                }
            elif rank == 3:
                converter_input_type = [
                    ct.TensorType(shape=(batch_coreml, seq_coreml, embedding_dim)) for _ in range(3)
                ]
                torch_export_dynamic_shapes = {
                    "query": {0: batch_torch, 1: seq_torch},
                    "key": {0: batch_torch, 1: seq_torch},
                    "value": {0: batch_torch, 1: seq_torch},
                }
            elif rank == 4:
                converter_input_type = [
                    ct.TensorType(shape=(batch_coreml, n_heads_1_coreml, seq_coreml, embedding_dim))
                    for _ in range(3)
                ]
                torch_export_dynamic_shapes = {
                    "query": {0: batch_torch, 1: n_heads_1_torch, 2: seq_torch},
                    "key": {0: batch_torch, 1: n_heads_1_torch, 2: seq_torch},
                    "value": {0: batch_torch, 1: n_heads_1_torch, 2: seq_torch},
                }
            else:
                assert rank == 5
                converter_input_type = [
                    ct.TensorType(
                        shape=(
                            batch_coreml,
                            n_heads_1_coreml,
                            n_heads_2_coreml,
                            seq_coreml,
                            embedding_dim,
                        )
                    )
                    for _ in range(3)
                ]
                torch_export_dynamic_shapes = {
                    "query": {0: batch_torch, 1: n_heads_1_torch, 2: n_heads_2_torch, 3: seq_torch},
                    "key": {0: batch_torch, 1: n_heads_1_torch, 2: n_heads_2_torch, 3: seq_torch},
                    "value": {0: batch_torch, 1: n_heads_1_torch, 2: n_heads_2_torch, 3: seq_torch},
                }
        else:
            converter_input_type = None
            torch_export_dynamic_shapes = None

        _, coreml_model, _, _, _, _ = self.run_compare_torch(
            [input_shape] * 3,
            model,
            frontend=frontend,
            backend=backend,
            converter_input_type=converter_input_type,
            torch_export_dynamic_shapes=torch_export_dynamic_shapes,
            compute_unit=compute_unit,
            minimum_deployment_target=minimum_deployment_target,
        )

        # mb.sdpa is introduced in iOS 18, so before iOS 18 we would decompose sdpa
        # torch.sdpa is not a core aten op, so executorch would decompose sdpa
        if (
            backend[0] == "mlprogram"
            and minimum_deployment_target == ct.target.iOS18
            and frontend != TorchFrontend.EXECUTORCH
        ):
            pymil_inputs = list(coreml_model._mil_program.functions["main"].inputs.values())
            is_io_fp16 = pymil_inputs[0].dtype == types.fp16
            is_io_precision_same_as_compute_precision = is_io_fp16 == (backend[1] == "fp16")
            if rank == 2:
                if is_io_precision_same_as_compute_precision:
                    expected_ops = [
                        "expand_dims",
                        "expand_dims",
                        "expand_dims",
                        "scaled_dot_product_attention",
                        "squeeze",
                    ]
                else:
                    expected_ops = [
                        "cast",
                        "expand_dims",
                        "cast",
                        "expand_dims",
                        "cast",
                        "expand_dims",
                        "scaled_dot_product_attention",
                        "squeeze",
                    ]
            else:
                if is_io_precision_same_as_compute_precision:
                    expected_ops = ["scaled_dot_product_attention"]
                else:
                    expected_ops = ["cast", "cast", "cast", "scaled_dot_product_attention"]
            assert get_op_types_in_program(coreml_model._mil_program) == expected_ops

        The query/key/value inputs can be any rank 2 or greater.
        
coremltools/converters/mil/frontend/torch/test/test_torch_ops.py