Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ Status UnaryOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const
coreml_op_type = "round";
} else if (op_type == "Exp") {
coreml_op_type = "exp";
} else if (op_type == "Sin") {
coreml_op_type = "sin";
} else if (op_type == "Cos") {
coreml_op_type = "cos";
} else if (op_type == "Ceil") {
coreml_op_type = "ceil";
} else {
Expand Down Expand Up @@ -89,8 +93,11 @@ Status UnaryOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const
bool UnaryOpBuilder::IsOpSupportedImpl(const Node& node, const OpBuilderInputParams& input_params,
const logging::Logger& /*logger*/) const {
if (!input_params.create_mlprogram) {
if (node.OpType() == "Erf" || node.OpType() == "Round" || node.OpType() == "Exp" ||
node.OpType() == "Ceil") {
// These ops only have an ML Program lowering; the NeuralNetwork
// UnaryFunctionLayerParams has no equivalent.
const auto& op_type = node.OpType();
if (op_type == "Erf" || op_type == "Round" || op_type == "Exp" ||
op_type == "Sin" || op_type == "Cos" || op_type == "Ceil") {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ static OpBuilderRegistrations CreateOpBuilderRegistrations() {
CreateUnaryOpBuilder("Round", op_registrations);
CreateUnaryOpBuilder("Sqrt", op_registrations);
CreateUnaryOpBuilder("Exp", op_registrations);
CreateUnaryOpBuilder("Sin", op_registrations);
CreateUnaryOpBuilder("Cos", op_registrations);
CreateUnaryOpBuilder("Ceil", op_registrations);

// Binary elementwise ops
Expand Down
59 changes: 59 additions & 0 deletions onnxruntime/test/providers/coreml/coreml_basic_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2360,6 +2360,55 @@ TEST(CoreMLExecutionProviderTest, Split11SingleOutputNotSupported) {
TestModelLoad(model_span, MakeCoreMLExecutionProvider("MLProgram"), ExpectedEPNodeAssignment::None);
}

namespace {
// Single-input model with both Sin and Cos consuming `X`, used by the
// Sin/Cos tests below.
std::string MakeSinCosModelData() {
onnxruntime::Model model("sin_cos_test", false, DefaultLoggingManager().DefaultLogger());
auto& graph = model.MainGraph();

ONNX_NAMESPACE::TypeProto float_tensor;
float_tensor.mutable_tensor_type()->set_elem_type(ONNX_NAMESPACE::TensorProto_DataType_FLOAT);
auto* shape = float_tensor.mutable_tensor_type()->mutable_shape();
shape->add_dim()->set_dim_value(1);
shape->add_dim()->set_dim_value(6);

auto& x = graph.GetOrCreateNodeArg("X", &float_tensor);
auto& sin_out = graph.GetOrCreateNodeArg("Sin_out", &float_tensor);
auto& cos_out = graph.GetOrCreateNodeArg("Cos_out", &float_tensor);
graph.AddNode("sin", "Sin", "sin node", {&x}, {&sin_out});
graph.AddNode("cos", "Cos", "cos node", {&x}, {&cos_out});

ORT_THROW_IF_ERROR(graph.Resolve());
std::string model_data;
model.ToProto().SerializeToString(&model_data);
return model_data;
}
} // namespace

// Sin and Cos are lowered to the ML Program 'sin' / 'cos' ops.
TEST(CoreMLExecutionProviderTest, SinCos_MLProgram) {
const std::string model_data = MakeSinCosModelData();
gsl::span<const std::byte> model_span{reinterpret_cast<const std::byte*>(model_data.data()),
model_data.size()};

#if defined(__APPLE__)
std::vector<int64_t> dims = {1, 6};
std::vector<float> values = {-2.0f, -0.5f, 0.0f, 0.5f, 1.0f, 2.0f};
OrtValue x_val;
CreateMLValue<float>(CPUAllocator::DefaultInstance(), dims, values, &x_val);
NameMLValMap feeds;
feeds.insert(std::make_pair("X", x_val));

EPVerificationParams params{};
params.ep_node_assignment = ExpectedEPNodeAssignment::All;
RunAndVerifyOutputsWithEP(model_span, CurrentTestName(),
MakeCoreMLExecutionProvider("MLProgram"), feeds, params);
#else
TestModelLoad(model_span, MakeCoreMLExecutionProvider("MLProgram"), ExpectedEPNodeAssignment::All);
#endif
}

TEST(CoreMLExecutionProviderTest, GatherScalarIndicesAxis1) {
// ai.onnx:Gather with rank-0 (scalar) 'indices'. ONNX output rank =
// data_rank + indices_rank - 1 = 2. The CoreML builder internally promotes
Expand Down Expand Up @@ -2437,6 +2486,16 @@ TEST(CoreMLExecutionProviderTest, GatherScalarIndicesAxis1) {
#endif
}

// Sin/Cos only have an ML Program lowering (the NeuralNetwork
// UnaryFunctionLayerParams has no sin/cos), so on the NeuralNetwork format
// they must fall back to CPU rather than be claimed.
TEST(CoreMLExecutionProviderTest, SinCosNeuralNetworkNotSupported) {
const std::string model_data = MakeSinCosModelData();
gsl::span<const std::byte> model_span{reinterpret_cast<const std::byte*>(model_data.data()),
model_data.size()};
TestModelLoad(model_span, MakeCoreMLExecutionProvider(), ExpectedEPNodeAssignment::None);
}

TEST(CoreMLExecutionProviderTest, GatherScalarIndicesAxis0) {
// Scalar Gather along axis 0 — squeeze axis is 0; covers a different
// squeeze position than the axis=1 test.
Expand Down
2 changes: 2 additions & 0 deletions tools/ci_build/github/apple/coreml_supported_mlprogram_ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Keep in sync with doco generated from /docs/execution-providers/CoreML-Execution
|ai.onnx:Concat||
|ai.onnx:Conv|Only 1D/2D Conv is supported.<br/>Bias if provided must be constant.|
|ai.onnx:ConvTranspose|Weight and bias must be constant.<br/>padding_type of SAME_UPPER/SAME_LOWER is not supported.<br/>kernel_shape must have default values.<br/>output_shape is not supported.<br/>output_padding must have default values.|
|ai.onnx:Cos||
|ai.onnx:DepthToSpace|If 'mode' is 'CRD' the input must have a fixed shape.|
|ai.onnx:Ceil||
|ai.onnx:Div||
Expand Down Expand Up @@ -43,6 +44,7 @@ Keep in sync with doco generated from /docs/execution-providers/CoreML-Execution
|ai.onnx:Resize|See [resize_op_builder.cc](https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/core/providers/coreml/builders/impl/resize_op_builder.cc) implementation. There are too many permutations to describe the valid combinations.|
|ai.onnx:Round||
|ai.onnx:Shape||
|ai.onnx:Sin||
|ai.onnx:Slice|starts/ends/axes/steps must be constant initializers.|
|ai.onnx:Softplus||
|ai.onnx:Split|If provided, `splits` must be constant.|
Expand Down
Loading