+1
-1
@@ -31,7 +31,7 @@ let package = Package(
|
|||||||
.target(name: "OnnxRuntimeBindings",
|
.target(name: "OnnxRuntimeBindings",
|
||||||
dependencies: ["onnxruntime"],
|
dependencies: ["onnxruntime"],
|
||||||
path: "objectivec",
|
path: "objectivec",
|
||||||
exclude: ["ReadMe.md", "format_objc.sh"],
|
exclude: ["test", "docs", "ReadMe.md", "format_objc.sh"],
|
||||||
cxxSettings: [
|
cxxSettings: [
|
||||||
.define("SPM_BUILD"),
|
.define("SPM_BUILD"),
|
||||||
.unsafeFlags(["-std=c++17",
|
.unsafeFlags(["-std=c++17",
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
static_assert(__has_feature(objc_arc), "Objective-C ARC must be enabled.");
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
module: ONNX Runtime Objective-C API
|
||||||
|
author: ONNX Runtime Authors
|
||||||
|
author_url: https://www.onnxruntime.ai
|
||||||
|
github_url: https://github.com/microsoft/onnxruntime
|
||||||
|
|
||||||
|
objc: true
|
||||||
|
umbrella_header: ../include/onnxruntime.h
|
||||||
|
framework_root: ..
|
||||||
|
|
||||||
|
readme: ./main_page.md
|
||||||
|
|
||||||
|
hide_documentation_coverage: true
|
||||||
|
undocumented_text: ""
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# ONNX Runtime Objective-C API
|
||||||
|
|
||||||
|
ONNX Runtime provides an Objective-C API.
|
||||||
|
|
||||||
|
It can be used from Objective-C/C++ or Swift with a bridging header.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Objective-C API Documentation
|
||||||
|
|
||||||
|
The API should be documented with comments in the [public header files](../include).
|
||||||
|
|
||||||
|
## Documentation Generation
|
||||||
|
|
||||||
|
The [Jazzy](https://github.com/realm/jazzy) tool is used to generate documentation from the code.
|
||||||
|
|
||||||
|
To generate documentation, from the repo root, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jazzy --config objectivec/docs/jazzy_config.yaml --output <output directory>
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated documentation website files will be in `<output directory>`.
|
||||||
|
|
||||||
|
[main_page.md](./main_page.md) contains content for the main page of the generated documentation website.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import "error_utils.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
static NSString* const kOrtErrorDomain = @"onnxruntime";
|
||||||
|
|
||||||
|
void ORTSaveCodeAndDescriptionToError(int code, const char* descriptionCstr, NSError** error) {
|
||||||
|
if (!error) return;
|
||||||
|
|
||||||
|
NSString* description = [NSString stringWithCString:descriptionCstr
|
||||||
|
encoding:NSASCIIStringEncoding];
|
||||||
|
|
||||||
|
*error = [NSError errorWithDomain:kOrtErrorDomain
|
||||||
|
code:code
|
||||||
|
userInfo:@{NSLocalizedDescriptionKey : description}];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ORTSaveOrtExceptionToError(const Ort::Exception& e, NSError** error) {
|
||||||
|
ORTSaveCodeAndDescriptionToError(e.GetOrtErrorCode(), e.what(), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ORTSaveExceptionToError(const std::exception& e, NSError** error) {
|
||||||
|
ORTSaveCodeAndDescriptionToError(ORT_RUNTIME_EXCEPTION, e.what(), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import "ort_coreml_execution_provider.h"
|
||||||
|
|
||||||
|
#import "cxx_api.h"
|
||||||
|
#import "error_utils.h"
|
||||||
|
#import "ort_session_internal.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
BOOL ORTIsCoreMLExecutionProviderAvailable() {
|
||||||
|
return ORT_OBJC_API_COREML_EP_AVAILABLE ? YES : NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation ORTCoreMLExecutionProviderOptions
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTSessionOptions (ORTSessionOptionsCoreMLEP)
|
||||||
|
|
||||||
|
- (BOOL)appendCoreMLExecutionProviderWithOptions:(ORTCoreMLExecutionProviderOptions*)options
|
||||||
|
error:(NSError**)error {
|
||||||
|
#if ORT_OBJC_API_COREML_EP_AVAILABLE
|
||||||
|
try {
|
||||||
|
const uint32_t flags =
|
||||||
|
(options.useCPUOnly ? COREML_FLAG_USE_CPU_ONLY : 0) |
|
||||||
|
(options.enableOnSubgraphs ? COREML_FLAG_ENABLE_ON_SUBGRAPH : 0) |
|
||||||
|
(options.onlyEnableForDevicesWithANE ? COREML_FLAG_ONLY_ENABLE_DEVICE_WITH_ANE : 0);
|
||||||
|
Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CoreML(
|
||||||
|
[self CXXAPIOrtSessionOptions], flags));
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error);
|
||||||
|
#else // !ORT_OBJC_API_COREML_EP_AVAILABLE
|
||||||
|
static_cast<void>(options);
|
||||||
|
ORTSaveCodeAndDescriptionToError(ORT_FAIL, "CoreML execution provider is not enabled.", error);
|
||||||
|
return NO;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import "ort_enums_internal.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#import "cxx_api.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct LoggingLevelInfo {
|
||||||
|
ORTLoggingLevel logging_level;
|
||||||
|
OrtLoggingLevel capi_logging_level;
|
||||||
|
};
|
||||||
|
|
||||||
|
// supported ORT logging levels
|
||||||
|
// define the mapping from ORTLoggingLevel to C API OrtLoggingLevel here
|
||||||
|
constexpr LoggingLevelInfo kLoggingLevelInfos[]{
|
||||||
|
{ORTLoggingLevelVerbose, ORT_LOGGING_LEVEL_VERBOSE},
|
||||||
|
{ORTLoggingLevelInfo, ORT_LOGGING_LEVEL_INFO},
|
||||||
|
{ORTLoggingLevelWarning, ORT_LOGGING_LEVEL_WARNING},
|
||||||
|
{ORTLoggingLevelError, ORT_LOGGING_LEVEL_ERROR},
|
||||||
|
{ORTLoggingLevelFatal, ORT_LOGGING_LEVEL_FATAL},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ValueTypeInfo {
|
||||||
|
ORTValueType type;
|
||||||
|
ONNXType capi_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// supported ORT value types
|
||||||
|
// define the mapping from ORTValueType to C API ONNXType here
|
||||||
|
constexpr ValueTypeInfo kValueTypeInfos[]{
|
||||||
|
{ORTValueTypeUnknown, ONNX_TYPE_UNKNOWN},
|
||||||
|
{ORTValueTypeTensor, ONNX_TYPE_TENSOR},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TensorElementTypeInfo {
|
||||||
|
ORTTensorElementDataType type;
|
||||||
|
ONNXTensorElementDataType capi_type;
|
||||||
|
size_t element_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
// supported ORT tensor element data types
|
||||||
|
// define the mapping from ORTTensorElementDataType to C API ONNXTensorElementDataType here
|
||||||
|
constexpr TensorElementTypeInfo kElementTypeInfos[]{
|
||||||
|
{ORTTensorElementDataTypeUndefined, ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED, 0},
|
||||||
|
{ORTTensorElementDataTypeFloat, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, sizeof(float)},
|
||||||
|
{ORTTensorElementDataTypeInt8, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8, sizeof(int8_t)},
|
||||||
|
{ORTTensorElementDataTypeUInt8, ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8, sizeof(uint8_t)},
|
||||||
|
{ORTTensorElementDataTypeInt32, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32, sizeof(int32_t)},
|
||||||
|
{ORTTensorElementDataTypeUInt32, ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32, sizeof(uint32_t)},
|
||||||
|
{ORTTensorElementDataTypeInt64, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64, sizeof(int64_t)},
|
||||||
|
{ORTTensorElementDataTypeUInt64, ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64, sizeof(uint64_t)},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GraphOptimizationLevelInfo {
|
||||||
|
ORTGraphOptimizationLevel opt_level;
|
||||||
|
GraphOptimizationLevel capi_opt_level;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ORT graph optimization levels
|
||||||
|
// define the mapping from ORTGraphOptimizationLevel to C API GraphOptimizationLevel here
|
||||||
|
constexpr GraphOptimizationLevelInfo kGraphOptimizationLevelInfos[]{
|
||||||
|
{ORTGraphOptimizationLevelNone, ORT_DISABLE_ALL},
|
||||||
|
{ORTGraphOptimizationLevelBasic, ORT_ENABLE_BASIC},
|
||||||
|
{ORTGraphOptimizationLevelExtended, ORT_ENABLE_EXTENDED},
|
||||||
|
{ORTGraphOptimizationLevelAll, ORT_ENABLE_ALL},
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container, typename SelectFn, typename TransformFn>
|
||||||
|
auto SelectAndTransform(
|
||||||
|
const Container& container, SelectFn select_fn, TransformFn transform_fn,
|
||||||
|
const char* not_found_msg)
|
||||||
|
-> decltype(transform_fn(*std::begin(container))) {
|
||||||
|
const auto it = std::find_if(
|
||||||
|
std::begin(container), std::end(container), select_fn);
|
||||||
|
if (it == std::end(container)) {
|
||||||
|
ORT_CXX_API_THROW(not_found_msg, ORT_NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
return transform_fn(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
OrtLoggingLevel PublicToCAPILoggingLevel(ORTLoggingLevel logging_level) {
|
||||||
|
return SelectAndTransform(
|
||||||
|
kLoggingLevelInfos,
|
||||||
|
[logging_level](const auto& logging_level_info) { return logging_level_info.logging_level == logging_level; },
|
||||||
|
[](const auto& logging_level_info) { return logging_level_info.capi_logging_level; },
|
||||||
|
"unsupported logging level");
|
||||||
|
}
|
||||||
|
|
||||||
|
ORTValueType CAPIToPublicValueType(ONNXType capi_type) {
|
||||||
|
return SelectAndTransform(
|
||||||
|
kValueTypeInfos,
|
||||||
|
[capi_type](const auto& type_info) { return type_info.capi_type == capi_type; },
|
||||||
|
[](const auto& type_info) { return type_info.type; },
|
||||||
|
"unsupported value type");
|
||||||
|
}
|
||||||
|
|
||||||
|
ONNXTensorElementDataType PublicToCAPITensorElementType(ORTTensorElementDataType type) {
|
||||||
|
return SelectAndTransform(
|
||||||
|
kElementTypeInfos,
|
||||||
|
[type](const auto& type_info) { return type_info.type == type; },
|
||||||
|
[](const auto& type_info) { return type_info.capi_type; },
|
||||||
|
"unsupported tensor element type");
|
||||||
|
}
|
||||||
|
|
||||||
|
ORTTensorElementDataType CAPIToPublicTensorElementType(ONNXTensorElementDataType capi_type) {
|
||||||
|
return SelectAndTransform(
|
||||||
|
kElementTypeInfos,
|
||||||
|
[capi_type](const auto& type_info) { return type_info.capi_type == capi_type; },
|
||||||
|
[](const auto& type_info) { return type_info.type; },
|
||||||
|
"unsupported tensor element type");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SizeOfCAPITensorElementType(ONNXTensorElementDataType capi_type) {
|
||||||
|
return SelectAndTransform(
|
||||||
|
kElementTypeInfos,
|
||||||
|
[capi_type](const auto& type_info) { return type_info.capi_type == capi_type; },
|
||||||
|
[](const auto& type_info) { return type_info.element_size; },
|
||||||
|
"unsupported tensor element type");
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphOptimizationLevel PublicToCAPIGraphOptimizationLevel(ORTGraphOptimizationLevel opt_level) {
|
||||||
|
return SelectAndTransform(
|
||||||
|
kGraphOptimizationLevelInfos,
|
||||||
|
[opt_level](const auto& opt_level_info) { return opt_level_info.opt_level == opt_level; },
|
||||||
|
[](const auto& opt_level_info) { return opt_level_info.capi_opt_level; },
|
||||||
|
"unsupported graph optimization level");
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import "ort_env_internal.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#import "cxx_api.h"
|
||||||
|
|
||||||
|
#import "error_utils.h"
|
||||||
|
#import "ort_enums_internal.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
NSString* ORTVersion(void) {
|
||||||
|
std::string result = OrtGetApiBase()->GetVersionString();
|
||||||
|
return [NSString stringWithUTF8String:result.c_str()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation ORTEnv {
|
||||||
|
std::optional<Ort::Env> _env;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithLoggingLevel:(ORTLoggingLevel)loggingLevel
|
||||||
|
error:(NSError**)error {
|
||||||
|
if ((self = [super init]) == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto CAPILoggingLevel = PublicToCAPILoggingLevel(loggingLevel);
|
||||||
|
_env = Ort::Env{CAPILoggingLevel};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (Ort::Env&)CXXAPIOrtEnv {
|
||||||
|
return *_env;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import "ort_session_internal.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#import "cxx_api.h"
|
||||||
|
#import "error_utils.h"
|
||||||
|
#import "ort_enums_internal.h"
|
||||||
|
#import "ort_env_internal.h"
|
||||||
|
#import "ort_value_internal.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
enum class NamedValueType {
|
||||||
|
Input,
|
||||||
|
OverridableInitializer,
|
||||||
|
Output,
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@implementation ORTSession {
|
||||||
|
std::optional<Ort::Session> _session;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithEnv:(ORTEnv*)env
|
||||||
|
modelPath:(NSString*)path
|
||||||
|
sessionOptions:(nullable ORTSessionOptions*)sessionOptions
|
||||||
|
error:(NSError**)error {
|
||||||
|
if ((self = [super init]) == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!sessionOptions) {
|
||||||
|
sessionOptions = [[ORTSessionOptions alloc] initWithError:error];
|
||||||
|
if (!sessionOptions) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_session = Ort::Session{[env CXXAPIOrtEnv],
|
||||||
|
path.UTF8String,
|
||||||
|
[sessionOptions CXXAPIOrtSessionOptions]};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)runWithInputs:(NSDictionary<NSString*, ORTValue*>*)inputs
|
||||||
|
outputs:(NSDictionary<NSString*, ORTValue*>*)outputs
|
||||||
|
runOptions:(nullable ORTRunOptions*)runOptions
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
if (!runOptions) {
|
||||||
|
runOptions = [[ORTRunOptions alloc] initWithError:error];
|
||||||
|
if (!runOptions) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const char*> inputNames, outputNames;
|
||||||
|
std::vector<const OrtValue*> inputValues;
|
||||||
|
std::vector<OrtValue*> outputValues;
|
||||||
|
|
||||||
|
for (NSString* inputName in inputs) {
|
||||||
|
inputNames.push_back(inputName.UTF8String);
|
||||||
|
inputValues.push_back(static_cast<const OrtValue*>([inputs[inputName] CXXAPIOrtValue]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NSString* outputName in outputs) {
|
||||||
|
outputNames.push_back(outputName.UTF8String);
|
||||||
|
outputValues.push_back(static_cast<OrtValue*>([outputs[outputName] CXXAPIOrtValue]));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ort::ThrowOnError(Ort::GetApi().Run(*_session, [runOptions CXXAPIOrtRunOptions],
|
||||||
|
inputNames.data(), inputValues.data(), inputNames.size(),
|
||||||
|
outputNames.data(), outputNames.size(), outputValues.data()));
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSDictionary<NSString*, ORTValue*>*)runWithInputs:(NSDictionary<NSString*, ORTValue*>*)inputs
|
||||||
|
outputNames:(NSSet<NSString*>*)outputNameSet
|
||||||
|
runOptions:(nullable ORTRunOptions*)runOptions
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
if (!runOptions) {
|
||||||
|
runOptions = [[ORTRunOptions alloc] initWithError:error];
|
||||||
|
if (!runOptions) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray<NSString*>* outputNameArray = outputNameSet.allObjects;
|
||||||
|
|
||||||
|
std::vector<const char*> inputNames, outputNames;
|
||||||
|
std::vector<const OrtValue*> inputValues;
|
||||||
|
std::vector<OrtValue*> outputValues;
|
||||||
|
|
||||||
|
for (NSString* inputName in inputs) {
|
||||||
|
inputNames.push_back(inputName.UTF8String);
|
||||||
|
inputValues.push_back(static_cast<const OrtValue*>([inputs[inputName] CXXAPIOrtValue]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NSString* outputName in outputNameArray) {
|
||||||
|
outputNames.push_back(outputName.UTF8String);
|
||||||
|
outputValues.push_back(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ort::ThrowOnError(Ort::GetApi().Run(*_session, [runOptions CXXAPIOrtRunOptions],
|
||||||
|
inputNames.data(), inputValues.data(), inputNames.size(),
|
||||||
|
outputNames.data(), outputNames.size(), outputValues.data()));
|
||||||
|
|
||||||
|
NSMutableDictionary<NSString*, ORTValue*>* outputs = [[NSMutableDictionary alloc] init];
|
||||||
|
for (NSUInteger i = 0; i < outputNameArray.count; ++i) {
|
||||||
|
ORTValue* outputValue = [[ORTValue alloc] initWithCAPIOrtValue:outputValues[i] externalTensorData:nil error:error];
|
||||||
|
if (!outputValue) {
|
||||||
|
// clean up remaining C API OrtValues which haven't been wrapped by an ORTValue yet
|
||||||
|
for (NSUInteger j = i; j < outputNameArray.count; ++j) {
|
||||||
|
Ort::GetApi().ReleaseValue(outputValues[j]);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputs[outputNameArray[i]] = outputValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSArray<NSString*>*)inputNamesWithError:(NSError**)error {
|
||||||
|
return [self namesWithType:NamedValueType::Input error:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSArray<NSString*>*)overridableInitializerNamesWithError:(NSError**)error {
|
||||||
|
return [self namesWithType:NamedValueType::OverridableInitializer error:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSArray<NSString*>*)outputNamesWithError:(NSError**)error {
|
||||||
|
return [self namesWithType:NamedValueType::Output error:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
|
- (nullable NSArray<NSString*>*)namesWithType:(NamedValueType)namedValueType
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
auto getCount = [&session = *_session, namedValueType]() {
|
||||||
|
if (namedValueType == NamedValueType::Input) {
|
||||||
|
return session.GetInputCount();
|
||||||
|
} else if (namedValueType == NamedValueType::OverridableInitializer) {
|
||||||
|
return session.GetOverridableInitializerCount();
|
||||||
|
} else {
|
||||||
|
return session.GetOutputCount();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getName = [&session = *_session, namedValueType](size_t i, OrtAllocator* allocator) {
|
||||||
|
if (namedValueType == NamedValueType::Input) {
|
||||||
|
return session.GetInputNameAllocated(i, allocator);
|
||||||
|
} else if (namedValueType == NamedValueType::OverridableInitializer) {
|
||||||
|
return session.GetOverridableInitializerNameAllocated(i, allocator);
|
||||||
|
} else {
|
||||||
|
return session.GetOutputNameAllocated(i, allocator);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t nameCount = getCount();
|
||||||
|
|
||||||
|
Ort::AllocatorWithDefaultOptions allocator;
|
||||||
|
NSMutableArray<NSString*>* result = [NSMutableArray arrayWithCapacity:nameCount];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nameCount; ++i) {
|
||||||
|
auto name = getName(i, allocator);
|
||||||
|
NSString* nameNsstr = [NSString stringWithUTF8String:name.get()];
|
||||||
|
NSAssert(nameNsstr != nil, @"nameNsstr must not be nil");
|
||||||
|
[result addObject:nameNsstr];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTSessionOptions {
|
||||||
|
std::optional<Ort::SessionOptions> _sessionOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithError:(NSError**)error {
|
||||||
|
if ((self = [super init]) == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_sessionOptions = Ort::SessionOptions{};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)appendExecutionProvider:(NSString*)providerName
|
||||||
|
providerOptions:(NSDictionary<NSString*, NSString*>*)providerOptions
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
std::unordered_map<std::string, std::string> options;
|
||||||
|
NSArray* keys = [providerOptions allKeys];
|
||||||
|
|
||||||
|
for (NSString* key in keys) {
|
||||||
|
NSString* value = [providerOptions objectForKey:key];
|
||||||
|
options.emplace(key.UTF8String, value.UTF8String);
|
||||||
|
}
|
||||||
|
|
||||||
|
_sessionOptions->AppendExecutionProvider(providerName.UTF8String, options);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)setIntraOpNumThreads:(int)intraOpNumThreads
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_sessionOptions->SetIntraOpNumThreads(intraOpNumThreads);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)setGraphOptimizationLevel:(ORTGraphOptimizationLevel)graphOptimizationLevel
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_sessionOptions->SetGraphOptimizationLevel(
|
||||||
|
PublicToCAPIGraphOptimizationLevel(graphOptimizationLevel));
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)setOptimizedModelFilePath:(NSString*)optimizedModelFilePath
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_sessionOptions->SetOptimizedModelFilePath(optimizedModelFilePath.UTF8String);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)setLogID:(NSString*)logID
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_sessionOptions->SetLogId(logID.UTF8String);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)setLogSeverityLevel:(ORTLoggingLevel)loggingLevel
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_sessionOptions->SetLogSeverityLevel(PublicToCAPILoggingLevel(loggingLevel));
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)addConfigEntryWithKey:(NSString*)key
|
||||||
|
value:(NSString*)value
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_sessionOptions->AddConfigEntry(key.UTF8String, value.UTF8String);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)registerCustomOpsUsingFunction:(NSString*)registrationFuncName
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_sessionOptions->RegisterCustomOpsUsingFunction(registrationFuncName.UTF8String);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Internal
|
||||||
|
|
||||||
|
- (Ort::SessionOptions&)CXXAPIOrtSessionOptions {
|
||||||
|
return *_sessionOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTRunOptions {
|
||||||
|
std::optional<Ort::RunOptions> _runOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithError:(NSError**)error {
|
||||||
|
if ((self = [super init]) == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_runOptions = Ort::RunOptions{};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)setLogTag:(NSString*)logTag
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_runOptions->SetRunTag(logTag.UTF8String);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)setLogSeverityLevel:(ORTLoggingLevel)loggingLevel
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_runOptions->SetRunLogSeverityLevel(PublicToCAPILoggingLevel(loggingLevel));
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)addConfigEntryWithKey:(NSString*)key
|
||||||
|
value:(NSString*)value
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
_runOptions->AddConfigEntry(key.UTF8String, value.UTF8String);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Internal
|
||||||
|
|
||||||
|
- (Ort::RunOptions&)CXXAPIOrtRunOptions {
|
||||||
|
return *_runOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import "ort_value_internal.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#import "cxx_api.h"
|
||||||
|
#import "error_utils.h"
|
||||||
|
#import "ort_enums_internal.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
ORTTensorTypeAndShapeInfo* CXXAPIToPublicTensorTypeAndShapeInfo(
|
||||||
|
const Ort::ConstTensorTypeAndShapeInfo& CXXAPITensorTypeAndShapeInfo) {
|
||||||
|
auto* result = [[ORTTensorTypeAndShapeInfo alloc] init];
|
||||||
|
const auto elementType = CXXAPITensorTypeAndShapeInfo.GetElementType();
|
||||||
|
const std::vector<int64_t> shape = CXXAPITensorTypeAndShapeInfo.GetShape();
|
||||||
|
|
||||||
|
result.elementType = CAPIToPublicTensorElementType(elementType);
|
||||||
|
auto* shapeArray = [[NSMutableArray alloc] initWithCapacity:shape.size()];
|
||||||
|
for (size_t i = 0; i < shape.size(); ++i) {
|
||||||
|
shapeArray[i] = @(shape[i]);
|
||||||
|
}
|
||||||
|
result.shape = shapeArray;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ORTValueTypeInfo* CXXAPIToPublicValueTypeInfo(
|
||||||
|
const Ort::TypeInfo& CXXAPITypeInfo) {
|
||||||
|
auto* result = [[ORTValueTypeInfo alloc] init];
|
||||||
|
const auto valueType = CXXAPITypeInfo.GetONNXType();
|
||||||
|
|
||||||
|
result.type = CAPIToPublicValueType(valueType);
|
||||||
|
|
||||||
|
if (valueType == ONNX_TYPE_TENSOR) {
|
||||||
|
const auto tensorTypeAndShapeInfo = CXXAPITypeInfo.GetTensorTypeAndShapeInfo();
|
||||||
|
result.tensorTypeAndShapeInfo = CXXAPIToPublicTensorTypeAndShapeInfo(tensorTypeAndShapeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// out = a * b
|
||||||
|
// returns true iff the result does not overflow
|
||||||
|
bool SafeMultiply(size_t a, size_t b, size_t& out) {
|
||||||
|
return !__builtin_mul_overflow(a, b, &out);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
@interface ORTValue ()
|
||||||
|
|
||||||
|
// pointer to any external tensor data to keep alive for the lifetime of the ORTValue
|
||||||
|
@property(nonatomic, nullable) NSMutableData* externalTensorData;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTValue {
|
||||||
|
std::optional<Ort::Value> _value;
|
||||||
|
std::optional<Ort::TypeInfo> _typeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithTensorData:(NSMutableData*)tensorData
|
||||||
|
elementType:(ORTTensorElementDataType)elementType
|
||||||
|
shape:(NSArray<NSNumber*>*)shape
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
const auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||||
|
const auto ONNXElementType = PublicToCAPITensorElementType(elementType);
|
||||||
|
const auto shapeVector = [shape]() {
|
||||||
|
std::vector<int64_t> result{};
|
||||||
|
result.reserve(shape.count);
|
||||||
|
for (NSNumber* dim in shape) {
|
||||||
|
result.push_back(dim.longLongValue);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
Ort::Value ortValue = Ort::Value::CreateTensor(
|
||||||
|
memoryInfo, tensorData.mutableBytes, tensorData.length,
|
||||||
|
shapeVector.data(), shapeVector.size(), ONNXElementType);
|
||||||
|
|
||||||
|
return [self initWithCAPIOrtValue:ortValue.release()
|
||||||
|
externalTensorData:tensorData
|
||||||
|
error:error];
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable ORTValueTypeInfo*)typeInfoWithError:(NSError**)error {
|
||||||
|
try {
|
||||||
|
return CXXAPIToPublicValueTypeInfo(*_typeInfo);
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable ORTTensorTypeAndShapeInfo*)tensorTypeAndShapeInfoWithError:(NSError**)error {
|
||||||
|
try {
|
||||||
|
const auto tensorTypeAndShapeInfo = _typeInfo->GetTensorTypeAndShapeInfo();
|
||||||
|
return CXXAPIToPublicTensorTypeAndShapeInfo(tensorTypeAndShapeInfo);
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSMutableData*)tensorDataWithError:(NSError**)error {
|
||||||
|
try {
|
||||||
|
const auto tensorTypeAndShapeInfo = _typeInfo->GetTensorTypeAndShapeInfo();
|
||||||
|
const size_t elementCount = tensorTypeAndShapeInfo.GetElementCount();
|
||||||
|
const size_t elementSize = SizeOfCAPITensorElementType(tensorTypeAndShapeInfo.GetElementType());
|
||||||
|
size_t rawDataLength;
|
||||||
|
if (!SafeMultiply(elementCount, elementSize, rawDataLength)) {
|
||||||
|
ORT_CXX_API_THROW("failed to compute tensor data length", ORT_RUNTIME_EXCEPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* rawData;
|
||||||
|
Ort::ThrowOnError(Ort::GetApi().GetTensorMutableData(*_value, &rawData));
|
||||||
|
|
||||||
|
return [NSMutableData dataWithBytesNoCopy:rawData
|
||||||
|
length:rawDataLength
|
||||||
|
freeWhenDone:NO];
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Internal
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithCAPIOrtValue:(OrtValue*)CAPIOrtValue
|
||||||
|
externalTensorData:(nullable NSMutableData*)externalTensorData
|
||||||
|
error:(NSError**)error {
|
||||||
|
if ((self = [super init]) == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_value = Ort::Value{CAPIOrtValue};
|
||||||
|
_typeInfo = _value->GetTypeInfo();
|
||||||
|
_externalTensorData = externalTensorData;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (Ort::Value&)CXXAPIOrtValue {
|
||||||
|
return *_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTValueTypeInfo
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTTensorTypeAndShapeInfo
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import "ort_xnnpack_execution_provider.h"
|
||||||
|
|
||||||
|
#import "cxx_api.h"
|
||||||
|
#import "error_utils.h"
|
||||||
|
#import "ort_session_internal.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@implementation ORTXnnpackExecutionProviderOptions
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTSessionOptions (ORTSessionOptionsXnnpackEP)
|
||||||
|
|
||||||
|
- (BOOL)appendXnnpackExecutionProviderWithOptions:(ORTXnnpackExecutionProviderOptions*)options
|
||||||
|
error:(NSError**)error {
|
||||||
|
try {
|
||||||
|
NSDictionary* provider_options = @{
|
||||||
|
@"intra_op_num_threads" : [NSString stringWithFormat:@"%d", options.intra_op_num_threads]
|
||||||
|
};
|
||||||
|
return [self appendExecutionProvider:@"XNNPACK" providerOptions:provider_options error:error];
|
||||||
|
}
|
||||||
|
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
static_assert(__has_feature(objc_arc), "Objective-C ARC must be enabled.");
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
#define ORTAssertNullableResultSuccessful(result, error) \
|
||||||
|
do { \
|
||||||
|
XCTAssertNotNil(result, @"Expected non-nil result but got nil. Error: %@", error); \
|
||||||
|
XCTAssertNil(error); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ORTAssertBoolResultSuccessful(result, error) \
|
||||||
|
do { \
|
||||||
|
XCTAssertTrue(result, @"Expected true result but got false. Error: %@", error); \
|
||||||
|
XCTAssertNil(error); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ORTAssertNullableResultUnsuccessful(result, error) \
|
||||||
|
do { \
|
||||||
|
XCTAssertNil(result); \
|
||||||
|
XCTAssertNotNil(error); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ORTAssertBoolResultUnsuccessful(result, error) \
|
||||||
|
do { \
|
||||||
|
XCTAssertFalse(result); \
|
||||||
|
XCTAssertNotNil(error); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
#import "ort_env.h"
|
||||||
|
|
||||||
|
#import "test/assertion_utils.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ORTEnvTest : XCTestCase
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTEnvTest
|
||||||
|
- (void)testGetOrtVersion {
|
||||||
|
NSString* ver = ORTVersion();
|
||||||
|
XCTAssertNotNil(ver);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testInitOk {
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTEnv* env = [[ORTEnv alloc] initWithLoggingLevel:ORTLoggingLevelWarning
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(env, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
#import "ort_coreml_execution_provider.h"
|
||||||
|
#import "ort_xnnpack_execution_provider.h"
|
||||||
|
#import "ort_env.h"
|
||||||
|
#import "ort_session.h"
|
||||||
|
#import "ort_value.h"
|
||||||
|
|
||||||
|
#import "test/assertion_utils.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ORTSessionTest : XCTestCase
|
||||||
|
|
||||||
|
@property(readonly, nullable) ORTEnv* ortEnv;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTSessionTest
|
||||||
|
|
||||||
|
- (void)setUp {
|
||||||
|
[super setUp];
|
||||||
|
|
||||||
|
self.continueAfterFailure = NO;
|
||||||
|
|
||||||
|
NSError* err = nil;
|
||||||
|
_ortEnv = [[ORTEnv alloc] initWithLoggingLevel:ORTLoggingLevelWarning
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(_ortEnv, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tearDown {
|
||||||
|
_ortEnv = nil;
|
||||||
|
|
||||||
|
[super tearDown];
|
||||||
|
}
|
||||||
|
|
||||||
|
// model with an Add op
|
||||||
|
// inputs: A, B
|
||||||
|
// output: C = A + B
|
||||||
|
+ (NSString*)getAddModelPath {
|
||||||
|
NSBundle* bundle = [NSBundle bundleForClass:[ORTSessionTest class]];
|
||||||
|
NSString* path = [bundle pathForResource:@"single_add.basic"
|
||||||
|
ofType:@"ort"];
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSMutableData*)dataWithScalarFloat:(float)value {
|
||||||
|
NSMutableData* data = [[NSMutableData alloc] initWithBytes:&value length:sizeof(value)];
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (ORTValue*)ortValueWithScalarFloatData:(NSMutableData*)data {
|
||||||
|
NSArray<NSNumber*>* shape = @[ @1 ];
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTValue* ortValue = [[ORTValue alloc] initWithTensorData:data
|
||||||
|
elementType:ORTTensorElementDataTypeFloat
|
||||||
|
shape:shape
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(ortValue, err);
|
||||||
|
return ortValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (ORTSessionOptions*)makeSessionOptions {
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTSessionOptions* sessionOptions = [[ORTSessionOptions alloc] initWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(sessionOptions, err);
|
||||||
|
return sessionOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (ORTRunOptions*)makeRunOptions {
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTRunOptions* runOptions = [[ORTRunOptions alloc] initWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(runOptions, err);
|
||||||
|
return runOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testInitAndRunWithPreallocatedOutputOk {
|
||||||
|
NSMutableData* aData = [ORTSessionTest dataWithScalarFloat:1.0f];
|
||||||
|
NSMutableData* bData = [ORTSessionTest dataWithScalarFloat:2.0f];
|
||||||
|
NSMutableData* cData = [ORTSessionTest dataWithScalarFloat:0.0f];
|
||||||
|
|
||||||
|
ORTValue* a = [ORTSessionTest ortValueWithScalarFloatData:aData];
|
||||||
|
ORTValue* b = [ORTSessionTest ortValueWithScalarFloatData:bData];
|
||||||
|
ORTValue* c = [ORTSessionTest ortValueWithScalarFloatData:cData];
|
||||||
|
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||||
|
modelPath:[ORTSessionTest getAddModelPath]
|
||||||
|
sessionOptions:[ORTSessionTest makeSessionOptions]
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(session, err);
|
||||||
|
|
||||||
|
BOOL runResult = [session runWithInputs:@{@"A" : a, @"B" : b}
|
||||||
|
outputs:@{@"C" : c}
|
||||||
|
runOptions:[ORTSessionTest makeRunOptions]
|
||||||
|
error:&err];
|
||||||
|
ORTAssertBoolResultSuccessful(runResult, err);
|
||||||
|
|
||||||
|
const float cExpected = 3.0f;
|
||||||
|
float cActual;
|
||||||
|
memcpy(&cActual, cData.bytes, sizeof(float));
|
||||||
|
XCTAssertEqual(cActual, cExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testInitAndRunOk {
|
||||||
|
NSMutableData* aData = [ORTSessionTest dataWithScalarFloat:1.0f];
|
||||||
|
NSMutableData* bData = [ORTSessionTest dataWithScalarFloat:2.0f];
|
||||||
|
|
||||||
|
ORTValue* a = [ORTSessionTest ortValueWithScalarFloatData:aData];
|
||||||
|
ORTValue* b = [ORTSessionTest ortValueWithScalarFloatData:bData];
|
||||||
|
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||||
|
modelPath:[ORTSessionTest getAddModelPath]
|
||||||
|
sessionOptions:[ORTSessionTest makeSessionOptions]
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(session, err);
|
||||||
|
|
||||||
|
NSDictionary<NSString*, ORTValue*>* outputs =
|
||||||
|
[session runWithInputs:@{@"A" : a, @"B" : b}
|
||||||
|
outputNames:[NSSet setWithArray:@[ @"C" ]]
|
||||||
|
runOptions:[ORTSessionTest makeRunOptions]
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(outputs, err);
|
||||||
|
|
||||||
|
ORTValue* cOutput = outputs[@"C"];
|
||||||
|
XCTAssertNotNil(cOutput);
|
||||||
|
|
||||||
|
NSData* cData = [cOutput tensorDataWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(cData, err);
|
||||||
|
|
||||||
|
const float cExpected = 3.0f;
|
||||||
|
float cActual;
|
||||||
|
memcpy(&cActual, cData.bytes, sizeof(float));
|
||||||
|
XCTAssertEqual(cActual, cExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testGetNamesOk {
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||||
|
modelPath:[ORTSessionTest getAddModelPath]
|
||||||
|
sessionOptions:[ORTSessionTest makeSessionOptions]
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(session, err);
|
||||||
|
|
||||||
|
NSArray<NSString*>* inputNames = [session inputNamesWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(inputNames, err);
|
||||||
|
XCTAssertEqualObjects(inputNames, (@[ @"A", @"B" ]));
|
||||||
|
|
||||||
|
NSArray<NSString*>* overridableInitializerNames = [session overridableInitializerNamesWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(overridableInitializerNames, err);
|
||||||
|
XCTAssertEqualObjects(overridableInitializerNames, (@[]));
|
||||||
|
|
||||||
|
NSArray<NSString*>* outputNames = [session outputNamesWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(outputNames, err);
|
||||||
|
XCTAssertEqualObjects(outputNames, (@[ @"C" ]));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testInitFailsWithInvalidPath {
|
||||||
|
NSString* invalidModelPath = @"invalid/path/to/model.ort";
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||||
|
modelPath:invalidModelPath
|
||||||
|
sessionOptions:[ORTSessionTest makeSessionOptions]
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultUnsuccessful(session, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRunFailsWithInvalidInput {
|
||||||
|
NSMutableData* dData = [ORTSessionTest dataWithScalarFloat:1.0f];
|
||||||
|
NSMutableData* cData = [ORTSessionTest dataWithScalarFloat:0.0f];
|
||||||
|
|
||||||
|
ORTValue* d = [ORTSessionTest ortValueWithScalarFloatData:dData];
|
||||||
|
ORTValue* c = [ORTSessionTest ortValueWithScalarFloatData:cData];
|
||||||
|
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||||
|
modelPath:[ORTSessionTest getAddModelPath]
|
||||||
|
sessionOptions:[ORTSessionTest makeSessionOptions]
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(session, err);
|
||||||
|
|
||||||
|
BOOL runResult = [session runWithInputs:@{@"D" : d}
|
||||||
|
outputs:@{@"C" : c}
|
||||||
|
runOptions:[ORTSessionTest makeRunOptions]
|
||||||
|
error:&err];
|
||||||
|
ORTAssertBoolResultUnsuccessful(runResult, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testAppendCoreMLEP {
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTSessionOptions* sessionOptions = [ORTSessionTest makeSessionOptions];
|
||||||
|
ORTCoreMLExecutionProviderOptions* coreMLOptions = [[ORTCoreMLExecutionProviderOptions alloc] init];
|
||||||
|
coreMLOptions.enableOnSubgraphs = YES; // set an arbitrary option
|
||||||
|
|
||||||
|
BOOL appendResult = [sessionOptions appendCoreMLExecutionProviderWithOptions:coreMLOptions
|
||||||
|
error:&err];
|
||||||
|
|
||||||
|
if (!ORTIsCoreMLExecutionProviderAvailable()) {
|
||||||
|
ORTAssertBoolResultUnsuccessful(appendResult, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ORTAssertBoolResultSuccessful(appendResult, err);
|
||||||
|
|
||||||
|
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||||
|
modelPath:[ORTSessionTest getAddModelPath]
|
||||||
|
sessionOptions:sessionOptions
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(session, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testAppendXnnpackEP {
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTSessionOptions* sessionOptions = [ORTSessionTest makeSessionOptions];
|
||||||
|
ORTXnnpackExecutionProviderOptions* XnnpackOptions = [[ORTXnnpackExecutionProviderOptions alloc] init];
|
||||||
|
XnnpackOptions.intra_op_num_threads = 2;
|
||||||
|
|
||||||
|
BOOL appendResult = [sessionOptions appendXnnpackExecutionProviderWithOptions:XnnpackOptions
|
||||||
|
error:&err];
|
||||||
|
// Without xnnpack EP in building also can pass the test
|
||||||
|
NSString* err_msg = [err localizedDescription];
|
||||||
|
if (!appendResult && [err_msg containsString:@"XNNPACK execution provider is not supported in this build. "]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ORTAssertBoolResultSuccessful(appendResult, err);
|
||||||
|
|
||||||
|
ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv
|
||||||
|
modelPath:[ORTSessionTest getAddModelPath]
|
||||||
|
sessionOptions:sessionOptions
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(session, err);
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
#import "ort_value.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#import "test/assertion_utils.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ORTValueTest : XCTestCase
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ORTValueTest
|
||||||
|
|
||||||
|
- (void)setUp {
|
||||||
|
[super setUp];
|
||||||
|
|
||||||
|
self.continueAfterFailure = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testInitTensorOk {
|
||||||
|
int32_t value = 42;
|
||||||
|
NSMutableData* data = [[NSMutableData alloc] initWithBytes:&value
|
||||||
|
length:sizeof(int32_t)];
|
||||||
|
NSArray<NSNumber*>* shape = @[ @1 ];
|
||||||
|
|
||||||
|
const ORTTensorElementDataType elementType = ORTTensorElementDataTypeInt32;
|
||||||
|
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTValue* ortValue = [[ORTValue alloc] initWithTensorData:data
|
||||||
|
elementType:elementType
|
||||||
|
shape:shape
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(ortValue, err);
|
||||||
|
|
||||||
|
auto checkTensorInfo = [&](ORTTensorTypeAndShapeInfo* tensorInfo) {
|
||||||
|
XCTAssertEqual(tensorInfo.elementType, elementType);
|
||||||
|
XCTAssertEqualObjects(tensorInfo.shape, shape);
|
||||||
|
};
|
||||||
|
|
||||||
|
ORTValueTypeInfo* typeInfo = [ortValue typeInfoWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(typeInfo, err);
|
||||||
|
XCTAssertEqual(typeInfo.type, ORTValueTypeTensor);
|
||||||
|
XCTAssertNotNil(typeInfo.tensorTypeAndShapeInfo);
|
||||||
|
checkTensorInfo(typeInfo.tensorTypeAndShapeInfo);
|
||||||
|
|
||||||
|
ORTTensorTypeAndShapeInfo* tensorInfo = [ortValue tensorTypeAndShapeInfoWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(tensorInfo, err);
|
||||||
|
checkTensorInfo(tensorInfo);
|
||||||
|
|
||||||
|
NSData* actualData = [ortValue tensorDataWithError:&err];
|
||||||
|
ORTAssertNullableResultSuccessful(actualData, err);
|
||||||
|
XCTAssertEqual(actualData.length, sizeof(int32_t));
|
||||||
|
int32_t actualValue;
|
||||||
|
memcpy(&actualValue, actualData.bytes, sizeof(int32_t));
|
||||||
|
XCTAssertEqual(actualValue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testInitTensorFailsWithDataSmallerThanShape {
|
||||||
|
std::vector<int32_t> values{1, 2, 3, 4};
|
||||||
|
NSMutableData* data = [[NSMutableData alloc] initWithBytes:values.data()
|
||||||
|
length:values.size() * sizeof(int32_t)];
|
||||||
|
NSArray<NSNumber*>* shape = @[ @2, @3 ]; // too large
|
||||||
|
|
||||||
|
NSError* err = nil;
|
||||||
|
ORTValue* ortValue = [[ORTValue alloc] initWithTensorData:data
|
||||||
|
elementType:ORTTensorElementDataTypeInt32
|
||||||
|
shape:shape
|
||||||
|
error:&err];
|
||||||
|
ORTAssertNullableResultUnsuccessful(ortValue, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Get directory this script is in
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
cd ${DIR}
|
||||||
|
|
||||||
|
python3 ./single_add_gen.py
|
||||||
|
|
||||||
|
ORT_CONVERT_ONNX_MODELS_TO_ORT_OPTIMIZATION_LEVEL=basic python3 -m onnxruntime.tools.convert_onnx_models_to_ort --optimization_style=Fixed .
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import onnx
|
||||||
|
from onnx import TensorProto, helper
|
||||||
|
|
||||||
|
graph = helper.make_graph(
|
||||||
|
[ # nodes
|
||||||
|
helper.make_node("Add", ["A", "B"], ["C"], "Add"),
|
||||||
|
],
|
||||||
|
"SingleAdd", # name
|
||||||
|
[ # inputs
|
||||||
|
helper.make_tensor_value_info("A", TensorProto.FLOAT, [1]),
|
||||||
|
helper.make_tensor_value_info("B", TensorProto.FLOAT, [1]),
|
||||||
|
],
|
||||||
|
[ # outputs
|
||||||
|
helper.make_tensor_value_info("C", TensorProto.FLOAT, [1]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 12)])
|
||||||
|
onnx.save(model, r"single_add.onnx")
|
||||||
Reference in New Issue
Block a user