pull swift/ and objc/ folder contents

This commit is contained in:
rachguo
2023-07-11 00:06:58 -07:00
parent 038da1b01a
commit bfd9340bfe
52 changed files with 3607 additions and 0 deletions
BIN
View File
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
NOTE: Flat directory structure to work with both the Objective-C build and the Swift Package Manager build which is done
via ../Package.swift
+4
View File
@@ -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.");
+40
View File
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// wrapper for ORT C/C++ API headers
#if defined(__clang__)
#pragma clang diagnostic push
// ignore clang documentation-related warnings
// instead, we will rely on Doxygen warnings for the C/C++ API headers
#pragma clang diagnostic ignored "-Wdocumentation"
#endif // defined(__clang__)
// paths are different when building the Swift Package Manager package as the headers come from the iOS pod archive
// clang-format off
#define STRINGIFY(x) #x
#ifdef SPM_BUILD
#define ORT_C_CXX_HEADER_FILE_PATH(x) STRINGIFY(onnxruntime/x)
#else
#define ORT_C_CXX_HEADER_FILE_PATH(x) STRINGIFY(x)
#endif
// clang-format on
#if __has_include(ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_training_c_api.h))
#include ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_training_c_api.h)
#include ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_training_cxx_api.h)
#else
#include ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_c_api.h)
#include ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_cxx_api.h)
#endif
#if __has_include(ORT_C_CXX_HEADER_FILE_PATH(coreml_provider_factory.h))
#define ORT_OBJC_API_COREML_EP_AVAILABLE 1
#include ORT_C_CXX_HEADER_FILE_PATH(coreml_provider_factory.h)
#else
#define ORT_OBJC_API_COREML_EP_AVAILABLE 0
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#endif // defined(__clang__)
+32
View File
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#include <optional>
#include <string>
#include <variant>
#import "cxx_api.h"
NS_ASSUME_NONNULL_BEGIN
@class ORTValue;
namespace utils {
NSString* toNSString(const std::string& str);
NSString* _Nullable toNullableNSString(const std::optional<std::string>& str);
std::string toStdString(NSString* str);
std::optional<std::string> toStdOptionalString(NSString* _Nullable str);
std::vector<std::string> toStdStringVector(NSArray<NSString*>* strs);
NSArray<NSString*>* toNSStringNSArray(const std::vector<std::string>& strs);
NSArray<ORTValue*>* _Nullable wrapUnownedCAPIOrtValues(const std::vector<OrtValue*>& values, NSError** error);
std::vector<const OrtValue*> getWrappedCAPIOrtValues(NSArray<ORTValue*>* values);
} // namespace utils
NS_ASSUME_NONNULL_END
+94
View File
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "cxx_utils.h"
#include <vector>
#include <optional>
#include <string>
#import "error_utils.h"
#import "ort_value_internal.h"
NS_ASSUME_NONNULL_BEGIN
namespace utils {
NSString* toNSString(const std::string& str) {
NSString* nsStr = [NSString stringWithUTF8String:str.c_str()];
if (!nsStr) {
ORT_CXX_API_THROW("Failed to convert std::string to NSString", ORT_INVALID_ARGUMENT);
}
return nsStr;
}
NSString* _Nullable toNullableNSString(const std::optional<std::string>& str) {
if (str.has_value()) {
return toNSString(*str);
}
return nil;
}
std::string toStdString(NSString* str) {
return std::string([str UTF8String]);
}
std::optional<std::string> toStdOptionalString(NSString* _Nullable str) {
if (str) {
return std::optional<std::string>([str UTF8String]);
}
return std::nullopt;
}
std::vector<std::string> toStdStringVector(NSArray<NSString*>* strs) {
std::vector<std::string> result;
result.reserve(strs.count);
for (NSString* str in strs) {
result.push_back([str UTF8String]);
}
return result;
}
NSArray<NSString*>* toNSStringNSArray(const std::vector<std::string>& strs) {
NSMutableArray<NSString*>* result = [NSMutableArray arrayWithCapacity:strs.size()];
for (const std::string& str : strs) {
[result addObject:toNSString(str)];
}
return result;
}
NSArray<ORTValue*>* _Nullable wrapUnownedCAPIOrtValues(const std::vector<OrtValue*>& CAPIValues, NSError** error) {
NSMutableArray<ORTValue*>* result = [NSMutableArray arrayWithCapacity:CAPIValues.size()];
for (size_t i = 0; i < CAPIValues.size(); ++i) {
// Wrap the C OrtValue in a C++ Ort::Value to automatically handle its release.
// Then, transfer that C++ Ort::Value to a new ORTValue.
Ort::Value CXXAPIValue{CAPIValues[i]};
ORTValue* val = [[ORTValue alloc] initWithCXXAPIOrtValue:std::move(CXXAPIValue)
externalTensorData:nil
error:error];
if (!val) {
// clean up remaining C OrtValues which haven't been wrapped by a C++ Ort::Value yet
for (size_t j = i + 1; j < CAPIValues.size(); ++j) {
Ort::GetApi().ReleaseValue(CAPIValues[j]);
}
return nil;
}
[result addObject:val];
}
return result;
}
std::vector<const OrtValue*> getWrappedCAPIOrtValues(NSArray<ORTValue*>* values) {
std::vector<const OrtValue*> result;
result.reserve(values.count);
for (ORTValue* val in values) {
result.push_back(static_cast<const OrtValue*>([val CXXAPIOrtValue]));
}
return result;
}
} // namespace utils
NS_ASSUME_NONNULL_END
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#include <exception>
#import "cxx_api.h"
NS_ASSUME_NONNULL_BEGIN
void ORTSaveCodeAndDescriptionToError(int code, const char* description, NSError** error);
void ORTSaveCodeAndDescriptionToError(int code, NSString* description, NSError** error);
void ORTSaveOrtExceptionToError(const Ort::Exception& e, NSError** error);
void ORTSaveExceptionToError(const std::exception& e, NSError** error);
// helper macros to catch and handle C++ exceptions
#define ORT_OBJC_API_IMPL_CATCH(error, failure_return_value) \
catch (const Ort::Exception& e) { \
ORTSaveOrtExceptionToError(e, (error)); \
return (failure_return_value); \
} \
catch (const std::exception& e) { \
ORTSaveExceptionToError(e, (error)); \
return (failure_return_value); \
}
#define ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error) \
ORT_OBJC_API_IMPL_CATCH(error, NO)
#define ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error) \
ORT_OBJC_API_IMPL_CATCH(error, nil)
NS_ASSUME_NONNULL_END
+37
View File
@@ -0,0 +1,37 @@
// 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 ORTSaveCodeAndDescriptionToError(int code, NSString* description, NSError** error) {
if (!error) return;
*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
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# formats Objective-C/C++ code
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
clang-format -i $(find ${SCRIPT_DIR} -name "*.h" -o -name "*.m" -o -name "*.mm")
+13
View File
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// this header contains the entire ONNX Runtime Objective-C API
// the headers below can also be imported individually
#import "ort_coreml_execution_provider.h"
#import "ort_custom_op_registration.h"
#import "ort_enums.h"
#import "ort_env.h"
#import "ort_session.h"
#import "ort_value.h"
#import "ort_xnnpack_execution_provider.h"
@@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// this header contains the entire ONNX Runtime training Objective-C API
// the headers below can also be imported individually
#import "onnxruntime.h"
#import "ort_checkpoint.h"
#import "ort_training_session.h"
+119
View File
@@ -0,0 +1,119 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#include <stdint.h>
NS_ASSUME_NONNULL_BEGIN
/**
* An ORT checkpoint is a snapshot of the state of a model at a given point in time.
*
* This class holds the entire training session state that includes model parameters,
* their gradients, optimizer parameters, and user properties. The `ORTTrainingSession` leverages the
* `ORTCheckpoint` by accessing and updating the contained training state.
*
* Available since 1.16.
*
* @note This class is only available when the training APIs are enabled.
*/
@interface ORTCheckpoint : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a checkpoint from directory on disk.
*
* @param path The path to the checkpoint directory.
* @param error Optional error information set if an error occurs.
* @return The instance, or nil if an error occurs.
*
* @warning The construction of the checkpoint state requires instantiation of `ORTEnv`.
* The intialization will fail if the `ORTEnv` is not properly initialized.
*/
- (nullable instancetype)initWithPath:(NSString*)path
error:(NSError**)error NS_DESIGNATED_INITIALIZER;
/**
* Saves a checkpoint to directory on disk.
*
* @param path The path to the checkpoint directory.
* @param includeOptimizerState Flag to indicate whether to save the optimizer state or not.
* @param error Optional error information set if an error occurs.
* @return Whether the checkpoint was saved successfully.
*/
- (BOOL)saveCheckpointToPath:(NSString*)path
withOptimizerState:(BOOL)includeOptimizerState
error:(NSError**)error;
/**
* Adds an int property to this checkpoint.
*
* @param name The name of the property.
* @param value The value of the property.
* @param error Optional error information set if an error occurs.
* @return Whether the property was added successfully.
*/
- (BOOL)addIntPropertyWithName:(NSString*)name
value:(int64_t)value
error:(NSError**)error;
/**
* Adds a float property to this checkpoint.
*
* @param name The name of the property.
* @param value The value of the property.
* @param error Optional error information set if an error occurs.
* @return Whether the property was added successfully.
*/
- (BOOL)addFloatPropertyWithName:(NSString*)name
value:(float)value
error:(NSError**)error;
/**
* Adds a string property to this checkpoint.
*
* @param name The name of the property.
* @param value The value of the property.
* @param error Optional error information set if an error occurs.
* @return Whether the property was added successfully.
*/
- (BOOL)addStringPropertyWithName:(NSString*)name
value:(NSString*)value
error:(NSError**)error;
/**
* Gets an int property from this checkpoint.
*
* @param name The name of the property.
* @param error Optional error information set if an error occurs.
* @return The value of the property or 0 if an error occurs.
*/
- (int64_t)getIntPropertyWithName:(NSString*)name
error:(NSError**)error __attribute__((swift_error(nonnull_error)));
/**
* Gets a float property from this checkpoint.
*
* @param name The name of the property.
* @param error Optional error information set if an error occurs.
* @return The value of the property or 0.0f if an error occurs.
*/
- (float)getFloatPropertyWithName:(NSString*)name
error:(NSError**)error __attribute__((swift_error(nonnull_error)));
/**
*
* Gets a string property from this checkpoint.
*
* @param name The name of the property.
* @param error Optional error information set if an error occurs.
* @return The value of the property.
*/
- (nullable NSString*)getStringPropertyWithName:(NSString*)name
error:(NSError**)error __attribute__((swift_error(nonnull_error)));
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#import "ort_session.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Gets whether the CoreML execution provider is available.
*/
BOOL ORTIsCoreMLExecutionProviderAvailable(void);
#ifdef __cplusplus
}
#endif
NS_ASSUME_NONNULL_BEGIN
/**
* Options for configuring the CoreML execution provider.
*/
@interface ORTCoreMLExecutionProviderOptions : NSObject
/**
* Whether the CoreML execution provider should run on CPU only.
*/
@property BOOL useCPUOnly;
/**
* Whether the CoreML execution provider is enabled on subgraphs.
*/
@property BOOL enableOnSubgraphs;
/**
* Whether the CoreML execution provider is only enabled for devices with Apple
* Neural Engine (ANE).
*/
@property BOOL onlyEnableForDevicesWithANE;
@end
@interface ORTSessionOptions (ORTSessionOptionsCoreMLEP)
/**
* Enables the CoreML execution provider in the session configuration options.
* It is appended to the execution provider list which is ordered by
* decreasing priority.
*
* @param options The CoreML execution provider configuration options.
* @param error Optional error information set if an error occurs.
* @return Whether the provider was enabled successfully.
*/
- (BOOL)appendCoreMLExecutionProviderWithOptions:(ORTCoreMLExecutionProviderOptions*)options
error:(NSError**)error;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/** C API type forward declaration. */
struct OrtStatus;
/** C API type forward declaration. */
struct OrtApiBase;
/** C API type forward declaration. */
struct OrtSessionOptions;
/**
* Pointer to a custom op registration function that uses the ONNX Runtime C API.
*
* The signature is defined in the ONNX Runtime C API:
* https://github.com/microsoft/onnxruntime/blob/67f4cd54fab321d83e4a75a40efeee95a6a17079/include/onnxruntime/core/session/onnxruntime_c_api.h#L697
*
* This is a low-level type intended for interoperating with libraries which provide such a function for custom op
* registration, such as [ONNX Runtime Extensions](https://github.com/microsoft/onnxruntime-extensions).
*/
typedef struct OrtStatus* (*ORTCAPIRegisterCustomOpsFnPtr)(struct OrtSessionOptions* /*options*/,
const struct OrtApiBase* /*api*/);
+55
View File
@@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* The ORT logging verbosity levels.
*/
typedef NS_ENUM(int32_t, ORTLoggingLevel) {
ORTLoggingLevelVerbose,
ORTLoggingLevelInfo,
ORTLoggingLevelWarning,
ORTLoggingLevelError,
ORTLoggingLevelFatal,
};
/**
* The ORT value types.
* Currently, a subset of all types is supported.
*/
typedef NS_ENUM(int32_t, ORTValueType) {
ORTValueTypeUnknown,
ORTValueTypeTensor,
};
/**
* The ORT tensor element data types.
* Currently, a subset of all types is supported.
*/
typedef NS_ENUM(int32_t, ORTTensorElementDataType) {
ORTTensorElementDataTypeUndefined,
ORTTensorElementDataTypeFloat,
ORTTensorElementDataTypeInt8,
ORTTensorElementDataTypeUInt8,
ORTTensorElementDataTypeInt32,
ORTTensorElementDataTypeUInt32,
ORTTensorElementDataTypeInt64,
ORTTensorElementDataTypeUInt64,
};
/**
* The ORT graph optimization levels.
* See here for more details:
* https://onnxruntime.ai/docs/performance/graph-optimizations.html
*/
typedef NS_ENUM(int32_t, ORTGraphOptimizationLevel) {
ORTGraphOptimizationLevelNone,
ORTGraphOptimizationLevelBasic,
ORTGraphOptimizationLevelExtended,
ORTGraphOptimizationLevelAll,
};
NS_ASSUME_NONNULL_END
+44
View File
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#import "ort_enums.h"
NS_ASSUME_NONNULL_BEGIN
#ifdef __cplusplus
extern "C" {
#endif
/**
* Gets the ORT version string in format major.minor.patch.
*
* Available since 1.15.
*/
NSString* _Nullable ORTVersion(void);
#ifdef __cplusplus
}
#endif
/**
* The ORT environment.
*/
@interface ORTEnv : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates an ORT Environment.
*
* @param loggingLevel The environment logging level.
* @param error Optional error information set if an error occurs.
* @return The instance, or nil if an error occurs.
*/
- (nullable instancetype)initWithLoggingLevel:(ORTLoggingLevel)loggingLevel
error:(NSError**)error NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END
+300
View File
@@ -0,0 +1,300 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#import "ort_custom_op_registration.h"
#import "ort_enums.h"
NS_ASSUME_NONNULL_BEGIN
@class ORTEnv;
@class ORTRunOptions;
@class ORTSessionOptions;
@class ORTValue;
/**
* An ORT session loads and runs a model.
*/
@interface ORTSession : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a session.
*
* @param env The ORT Environment instance.
* @param path The path to the ONNX model.
* @param sessionOptions Optional session configuration options.
* @param error Optional error information set if an error occurs.
* @return The instance, or nil if an error occurs.
*/
- (nullable instancetype)initWithEnv:(ORTEnv*)env
modelPath:(NSString*)path
sessionOptions:(nullable ORTSessionOptions*)sessionOptions
error:(NSError**)error NS_DESIGNATED_INITIALIZER;
/**
* Runs the model.
* The inputs and outputs are pre-allocated.
*
* @param inputs Dictionary of input names to input ORT values.
* @param outputs Dictionary of output names to output ORT values.
* @param runOptions Optional run configuration options.
* @param error Optional error information set if an error occurs.
* @return Whether the model was run successfully.
*/
- (BOOL)runWithInputs:(NSDictionary<NSString*, ORTValue*>*)inputs
outputs:(NSDictionary<NSString*, ORTValue*>*)outputs
runOptions:(nullable ORTRunOptions*)runOptions
error:(NSError**)error;
/**
* Runs the model.
* The inputs are pre-allocated and the outputs are allocated by ORT.
*
* @param inputs Dictionary of input names to input ORT values.
* @param outputNames Set of output names.
* @param runOptions Optional run configuration options.
* @param error Optional error information set if an error occurs.
* @return A dictionary of output names to output ORT values with the outputs
* requested in `outputNames`, or nil if an error occurs.
*/
- (nullable NSDictionary<NSString*, ORTValue*>*)runWithInputs:(NSDictionary<NSString*, ORTValue*>*)inputs
outputNames:(NSSet<NSString*>*)outputNames
runOptions:(nullable ORTRunOptions*)runOptions
error:(NSError**)error;
/**
* Gets the model's input names.
*
* @param error Optional error information set if an error occurs.
* @return An array of input names, or nil if an error occurs.
*/
- (nullable NSArray<NSString*>*)inputNamesWithError:(NSError**)error;
/**
* Gets the model's overridable initializer names.
*
* @param error Optional error information set if an error occurs.
* @return An array of overridable initializer names, or nil if an error occurs.
*/
- (nullable NSArray<NSString*>*)overridableInitializerNamesWithError:(NSError**)error;
/**
* Gets the model's output names.
*
* @param error Optional error information set if an error occurs.
* @return An array of output names, or nil if an error occurs.
*/
- (nullable NSArray<NSString*>*)outputNamesWithError:(NSError**)error;
@end
/**
* Options for configuring a session.
*/
@interface ORTSessionOptions : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates session configuration options.
*
* @param error Optional error information set if an error occurs.
* @return The instance, or nil if an error occurs.
*/
- (nullable instancetype)initWithError:(NSError**)error NS_SWIFT_NAME(init());
/**
* Appends an execution provider to the session options to enable the execution provider to be used when running
* the model.
*
* Available since 1.14.
*
* The execution provider list is ordered by decreasing priority.
* i.e. the first provider registered has the highest priority.
*
* @param providerName Provider name. For example, "xnnpack".
* @param providerOptions Provider-specific options. For example, for provider "xnnpack", {"intra_op_num_threads": "2"}.
* @param error Optional error information set if an error occurs.
* @return Whether the execution provider was appended successfully
*/
- (BOOL)appendExecutionProvider:(NSString*)providerName
providerOptions:(NSDictionary<NSString*, NSString*>*)providerOptions
error:(NSError**)error;
/**
* Sets the number of threads used to parallelize the execution within nodes.
* A value of 0 means ORT will pick a default value.
*
* @param intraOpNumThreads The number of threads.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)setIntraOpNumThreads:(int)intraOpNumThreads
error:(NSError**)error;
/**
* Sets the graph optimization level.
*
* @param graphOptimizationLevel The graph optimization level.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)setGraphOptimizationLevel:(ORTGraphOptimizationLevel)graphOptimizationLevel
error:(NSError**)error;
/**
* Sets the path to which the optimized model file will be saved.
*
* @param optimizedModelFilePath The optimized model file path.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)setOptimizedModelFilePath:(NSString*)optimizedModelFilePath
error:(NSError**)error;
/**
* Sets the session log ID.
*
* @param logID The log ID.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)setLogID:(NSString*)logID
error:(NSError**)error;
/**
* Sets the session log severity level.
*
* @param loggingLevel The log severity level.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)setLogSeverityLevel:(ORTLoggingLevel)loggingLevel
error:(NSError**)error;
/**
* Sets a session configuration key-value pair.
* Any value for a previously set key will be overwritten.
* The session configuration keys and values are documented here:
* https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h
*
* @param key The key.
* @param value The value.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)addConfigEntryWithKey:(NSString*)key
value:(NSString*)value
error:(NSError**)error;
/**
* Registers custom ops for use with `ORTSession`s using this SessionOptions by calling the specified
* native function name. The custom ops library must either be linked against, or have previously been loaded
* by the user.
*
* Available since 1.14.
*
* The registration function must have the signature:
* `OrtStatus* (*fn)(OrtSessionOptions* options, const OrtApiBase* api);`
*
* The signature is defined in the ONNX Runtime C API:
* https://github.com/microsoft/onnxruntime/blob/67f4cd54fab321d83e4a75a40efeee95a6a17079/include/onnxruntime/core/session/onnxruntime_c_api.h#L697
*
* See https://onnxruntime.ai/docs/reference/operators/add-custom-op.html for more information on custom ops.
* See https://github.com/microsoft/onnxruntime/blob/342a5bf2b756d1a1fc6fdc582cfeac15182632fe/onnxruntime/test/testdata/custom_op_library/custom_op_library.cc#L115
* for an example of a custom op library registration function.
*
* @note The caller must ensure that `registrationFuncName` names a valid function that is visible to the native ONNX
* Runtime code and has the correct signature.
* They must ensure that the function does what they expect it to do because this method will just call it.
*
* @param registrationFuncName The name of the registration function to call.
* @param error Optional error information set if an error occurs.
* @return Whether the registration function was successfully called.
*/
- (BOOL)registerCustomOpsUsingFunction:(NSString*)registrationFuncName
error:(NSError**)error;
/**
* Registers custom ops for use with `ORTSession`s using this SessionOptions by calling the specified function
* pointed to by `registerCustomOpsFn`.
*
* Available since 1.16.
*
* The registration function must have the signature:
* `OrtStatus* (*fn)(OrtSessionOptions* options, const OrtApiBase* api);`
*
* The signature is defined in the ONNX Runtime C API:
* https://github.com/microsoft/onnxruntime/blob/67f4cd54fab321d83e4a75a40efeee95a6a17079/include/onnxruntime/core/session/onnxruntime_c_api.h#L697
*
* See https://onnxruntime.ai/docs/reference/operators/add-custom-op.html for more information on custom ops.
* See https://github.com/microsoft/onnxruntime/blob/342a5bf2b756d1a1fc6fdc582cfeac15182632fe/onnxruntime/test/testdata/custom_op_library/custom_op_library.cc#L115
* for an example of a custom op library registration function.
*
* @note The caller must ensure that `registerCustomOpsFn` is a valid function pointer and has the correct signature.
* They must ensure that the function does what they expect it to do because this method will just call it.
*
* @param registerCustomOpsFn A pointer to the registration function to call.
* @param error Optional error information set if an error occurs.
* @return Whether the registration function was successfully called.
*/
- (BOOL)registerCustomOpsUsingFunctionPointer:(ORTCAPIRegisterCustomOpsFnPtr)registerCustomOpsFn
error:(NSError**)error;
@end
/**
* Options for configuring a run.
*/
@interface ORTRunOptions : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates run configuration options.
*
* @param error Optional error information set if an error occurs.
* @return The instance, or nil if an error occurs.
*/
- (nullable instancetype)initWithError:(NSError**)error NS_SWIFT_NAME(init());
/**
* Sets the run log tag.
*
* @param logTag The log tag.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)setLogTag:(NSString*)logTag
error:(NSError**)error;
/**
* Sets the run log severity level.
*
* @param loggingLevel The log severity level.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)setLogSeverityLevel:(ORTLoggingLevel)loggingLevel
error:(NSError**)error;
/**
* Sets a run configuration key-value pair.
* Any value for a previously set key will be overwritten.
* The run configuration keys and values are documented here:
* https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_run_options_config_keys.h
*
* @param key The key.
* @param value The value.
* @param error Optional error information set if an error occurs.
* @return Whether the option was set successfully.
*/
- (BOOL)addConfigEntryWithKey:(NSString*)key
value:(NSString*)value
error:(NSError**)error;
@end
NS_ASSUME_NONNULL_END
+263
View File
@@ -0,0 +1,263 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#include <stdint.h>
NS_ASSUME_NONNULL_BEGIN
@class ORTCheckpoint;
@class ORTEnv;
@class ORTValue;
@class ORTSessionOptions;
/**
* Trainer class that provides methods to train, evaluate and optimize ONNX models.
*
* The training session requires four training artifacts:
* 1. Training onnx model
* 2. Evaluation onnx model (optional)
* 3. Optimizer onnx model
* 4. Checkpoint directory
*
* [onnxruntime-training python utility](https://github.com/microsoft/onnxruntime/blob/main/orttraining/orttraining/python/training/onnxblock/README.md)
* can be used to generate above training artifacts.
*
* Available since 1.16.
*
* @note This class is only available when the training APIs are enabled.
*/
@interface ORTTrainingSession : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a training session from the training artifacts that can be used to begin or resume training.
*
* The initializer instantiates the training session based on provided env and session options, which can be used to
* begin or resume training from a given checkpoint state. The checkpoint state represents the parameters of training
* session which will be moved to the device specified in the session option if needed.
*
* @param env The `ORTEnv` instance to use for the training session.
* @param sessionOptions The `ORTSessionOptions` to use for the training session.
* @param checkpoint Training states that are used as a starting point for training.
* @param trainModelPath The path to the training onnx model.
* @param evalModelPath The path to the evaluation onnx model.
* @param optimizerModelPath The path to the optimizer onnx model used to perform gradient descent.
* @param error Optional error information set if an error occurs.
* @return The instance, or nil if an error occurs.
*
* @note Note that the training session created with a checkpoint state uses this state to store the entire training
* state (including model parameters, its gradients, the optimizer states and the properties). The training session
* keeps a strong (owning) pointer to the checkpoint state.
*/
- (nullable instancetype)initWithEnv:(ORTEnv*)env
sessionOptions:(ORTSessionOptions*)sessionOptions
checkpoint:(ORTCheckpoint*)checkpoint
trainModelPath:(NSString*)trainModelPath
evalModelPath:(nullable NSString*)evalModelPath
optimizerModelPath:(nullable NSString*)optimizerModelPath
error:(NSError**)error NS_DESIGNATED_INITIALIZER;
/**
* Performs a training step, which is equivalent to a forward and backward propagation in a single step.
*
* The training step computes the outputs of the training model and the gradients of the trainable parameters
* for the given input values. The train step is performed based on the training model that was provided to the training session.
* It is equivalent to running forward and backward propagation in a single step. The computed gradients are stored inside
* the training session state so they can be later consumed by `optimizerStep`. The gradients can be lazily reset by
* calling `lazyResetGrad` method.
*
* @param inputs The input values to the training model.
* @param error Optional error information set if an error occurs.
* @return The output values of the training model.
*/
- (nullable NSArray<ORTValue*>*)trainStepWithInputValues:(NSArray<ORTValue*>*)inputs
error:(NSError**)error;
/**
* Performs a evaluation step that computes the outputs of the evaluation model for the given inputs.
* The eval step is performed based on the evaluation model that was provided to the training session.
*
* @param inputs The input values to the eval model.
* @param error Optional error information set if an error occurs.
* @return The output values of the eval model.
*
*/
- (nullable NSArray<ORTValue*>*)evalStepWithInputValues:(NSArray<ORTValue*>*)inputs
error:(NSError**)error;
/**
* Reset the gradients of all trainable parameters to zero lazily.
*
* Calling this method sets the internal state of the training session such that the gradients of the trainable parameters
* in the ORTCheckpoint will be scheduled to be reset just before the new gradients are computed on the next
* invocation of the `trainStep` method.
*
* @param error Optional error information set if an error occurs.
* @return YES if the gradients are set to reset successfully, NO otherwise.
*/
- (BOOL)lazyResetGradWithError:(NSError**)error;
/**
* Performs the weight updates for the trainable parameters using the optimizer model. The optimizer step is performed
* based on the optimizer model that was provided to the training session. The updated parameters are stored inside the
* training state so that they can be used by the next `trainStep` method call.
*
* @param error Optional error information set if an error occurs.
* @return YES if the optimizer step was performed successfully, NO otherwise.
*/
- (BOOL)optimizerStepWithError:(NSError**)error;
/**
* Returns the names of the user inputs for the training model that can be associated with
* the `ORTValue` provided to the `trainStep`.
*
* @param error Optional error information set if an error occurs.
* @return The names of the user inputs for the training model.
*/
- (nullable NSArray<NSString*>*)getTrainInputNamesWithError:(NSError**)error;
/**
* Returns the names of the user inputs for the evaluation model that can be associated with
* the `ORTValue` provided to the `evalStep`.
*
* @param error Optional error information set if an error occurs.
* @return The names of the user inputs for the evaluation model.
*/
- (nullable NSArray<NSString*>*)getEvalInputNamesWithError:(NSError**)error;
/**
* Returns the names of the user outputs for the training model that can be associated with
* the `ORTValue` returned by the `trainStep`.
*
* @param error Optional error information set if an error occurs.
* @return The names of the user outputs for the training model.
*/
- (nullable NSArray<NSString*>*)getTrainOutputNamesWithError:(NSError**)error;
/**
* Returns the names of the user outputs for the evaluation model that can be associated with
* the `ORTValue` returned by the `evalStep`.
*
* @param error Optional error information set if an error occurs.
* @return The names of the user outputs for the evaluation model.
*/
- (nullable NSArray<NSString*>*)getEvalOutputNamesWithError:(NSError**)error;
/**
* Registers a linear learning rate scheduler for the training session.
*
* The scheduler gradually decreases the learning rate from the initial value to zero over the course of the training.
* The decrease is performed by multiplying the current learning rate by a linearly updated factor.
* Before the decrease, the learning rate is gradually increased from zero to the initial value during a warmup phase.
*
* @param warmupStepCount The number of steps to perform the linear warmup.
* @param totalStepCount The total number of steps to perform the linear decay.
* @param initialLr The initial learning rate.
* @param error Optional error information set if an error occurs.
* @return YES if the scheduler was registered successfully, NO otherwise.
*/
- (BOOL)registerLinearLRSchedulerWithWarmupStepCount:(int64_t)warmupStepCount
totalStepCount:(int64_t)totalStepCount
initialLr:(float)initialLr
error:(NSError**)error;
/**
* Update the learning rate based on the registered learning rate scheduler.
*
* Performs a scheduler step that updates the learning rate that is being used by the training session.
* This function should typically be called before invoking the optimizer step for each round, or as necessary
* to update the learning rate being used by the training session.
*
* @note A valid predefined learning rate scheduler must be first registered to invoke this method.
*
* @param error Optional error information set if an error occurs.
* @return YES if the scheduler step was performed successfully, NO otherwise.
*/
- (BOOL)schedulerStepWithError:(NSError**)error;
/**
* Returns the current learning rate being used by the training session.
*
* @param error Optional error information set if an error occurs.
* @return The current learning rate or 0.0f if an error occurs.
*/
- (float)getLearningRateWithError:(NSError**)error __attribute__((swift_error(nonnull_error)));
/**
* Sets the learning rate being used by the training session.
*
* The current learning rate is maintained by the training session and can be overwritten by invoking this method
* with the desired learning rate. This function should not be used when a valid learning rate scheduler is registered.
* It should be used either to set the learning rate derived from a custom learning rate scheduler or to set a constant
* learning rate to be used throughout the training session.
*
* @note It does not set the initial learning rate that may be needed by the predefined learning rate schedulers.
* To set the initial learning rate for learning rate schedulers, use the `registerLinearLRScheduler` method.
*
* @param lr The learning rate to be used by the training session.
* @param error Optional error information set if an error occurs.
* @return YES if the learning rate was set successfully, NO otherwise.
*/
- (BOOL)setLearningRate:(float)lr
error:(NSError**)error;
/**
* Loads the training session model parameters from a contiguous buffer.
*
* @param buffer Contiguous buffer to load the parameters from.
* @param error Optional error information set if an error occurs.
* @return YES if the parameters were loaded successfully, NO otherwise.
*/
- (BOOL)fromBufferWithValue:(ORTValue*)buffer
error:(NSError**)error;
/**
* Returns a contiguous buffer that holds a copy of all training state parameters.
*
* @param onlyTrainable If YES, returns a buffer that holds only the trainable parameters, otherwise returns a buffer
* that holds all the parameters.
* @param error Optional error information set if an error occurs.
* @return A contiguous buffer that holds a copy of all training state parameters.
*/
- (nullable ORTValue*)toBufferWithTrainable:(BOOL)onlyTrainable
error:(NSError**)error;
/**
* Exports the training session model that can be used for inference.
*
* If the training session was provided with an eval model, the training session can generate an inference model if it
* knows the inference graph outputs. The input inference graph outputs are used to prune the eval model so that the
* inference model's outputs align with the provided outputs. The exported model is saved at the path provided and
* can be used for inferencing with `ORTSession`.
*
* @note The method reloads the eval model from the path provided to the initializer and expects this path to be valid.
*
* @param inferenceModelPath The path to the serialized the inference model.
* @param graphOutputNames The names of the outputs that are needed in the inference model.
* @param error Optional error information set if an error occurs.
* @return YES if the inference model was exported successfully, NO otherwise.
*/
- (BOOL)exportModelForInferenceWithOutputPath:(NSString*)inferenceModelPath
graphOutputNames:(NSArray<NSString*>*)graphOutputNames
error:(NSError**)error;
@end
#ifdef __cplusplus
extern "C" {
#endif
/**
* This function sets the seed for generating random numbers.
* Use this function to generate reproducible results. It should be noted that completely reproducible results are not guaranteed.
*
* @param seed Manually set seed to use for random number generation.
*/
void ORTSetSeed(int64_t seed);
#ifdef __cplusplus
}
#endif
NS_ASSUME_NONNULL_END
+94
View File
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#import "ort_enums.h"
NS_ASSUME_NONNULL_BEGIN
@class ORTValueTypeInfo;
@class ORTTensorTypeAndShapeInfo;
/**
* An ORT value encapsulates data used as an input or output to a model at runtime.
*/
@interface ORTValue : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a value that is a tensor.
* The tensor data is allocated by the caller.
*
* @param tensorData The tensor data.
* @param elementType The tensor element data type.
* @param shape The tensor shape.
* @param error Optional error information set if an error occurs.
* @return The instance, or nil if an error occurs.
*/
- (nullable instancetype)initWithTensorData:(NSMutableData*)tensorData
elementType:(ORTTensorElementDataType)elementType
shape:(NSArray<NSNumber*>*)shape
error:(NSError**)error;
/**
* Gets the type information.
*
* @param error Optional error information set if an error occurs.
* @return The type information, or nil if an error occurs.
*/
- (nullable ORTValueTypeInfo*)typeInfoWithError:(NSError**)error;
/**
* Gets the tensor type and shape information.
* This assumes that the value is a tensor.
*
* @param error Optional error information set if an error occurs.
* @return The tensor type and shape information, or nil if an error occurs.
*/
- (nullable ORTTensorTypeAndShapeInfo*)tensorTypeAndShapeInfoWithError:(NSError**)error;
/**
* Gets the tensor data.
* This assumes that the value is a tensor.
*
* This returns the value's underlying data directly, not a copy of it.
* The memory's lifetime may be tied to this value, i.e., if it was allocated
* by ORT. On the other hand, the memory's lifetime is independent of the value
* if the value was created with user-provided data.
*
* @param error Optional error information set if an error occurs.
* @return The tensor data, or nil if an error occurs.
*/
- (nullable NSMutableData*)tensorDataWithError:(NSError**)error;
@end
/**
* A value's type information.
*/
@interface ORTValueTypeInfo : NSObject
/** The value type. */
@property(nonatomic) ORTValueType type;
/** The tensor type and shape information, if the value is a tensor. */
@property(nonatomic, nullable) ORTTensorTypeAndShapeInfo* tensorTypeAndShapeInfo;
@end
/**
* A tensor's type and shape information.
*/
@interface ORTTensorTypeAndShapeInfo : NSObject
/** The tensor element data type. */
@property(nonatomic) ORTTensorElementDataType elementType;
/** The tensor shape. */
@property(nonatomic) NSArray<NSNumber*>* shape;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#import "ort_session.h"
NS_ASSUME_NONNULL_BEGIN
/**
* Options for configuring the Xnnpack execution provider.
*/
@interface ORTXnnpackExecutionProviderOptions : NSObject
/**
* How many threads used for the Xnnpack execution provider.
*/
@property int intra_op_num_threads;
@end
@interface ORTSessionOptions (ORTSessionOptionsXnnpackEP)
/**
* Available since 1.14.
* Enables the Xnnpack execution provider in the session configuration options.
* It is appended to the execution provider list which is ordered by
* decreasing priority.
*
* @param options The Xnnpack execution provider configuration options.
* @param error Optional error information set if an error occurs.
* @return Whether the provider was enabled successfully.
*/
- (BOOL)appendXnnpackExecutionProviderWithOptions:(ORTXnnpackExecutionProviderOptions*)options
error:(NSError**)error;
@end
NS_ASSUME_NONNULL_END
+111
View File
@@ -0,0 +1,111 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ort_checkpoint_internal.h"
#include <optional>
#include <string>
#include <variant>
#import "cxx_api.h"
#import "error_utils.h"
NS_ASSUME_NONNULL_BEGIN
@implementation ORTCheckpoint {
std::optional<Ort::CheckpointState> _checkpoint;
}
- (nullable instancetype)initWithPath:(NSString*)path
error:(NSError**)error {
if ((self = [super init]) == nil) {
return nil;
}
try {
_checkpoint = Ort::CheckpointState::LoadCheckpoint(path.UTF8String);
return self;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (BOOL)saveCheckpointToPath:(NSString*)path
withOptimizerState:(BOOL)includeOptimizerState
error:(NSError**)error {
try {
Ort::CheckpointState::SaveCheckpoint([self CXXAPIOrtCheckpoint], path.UTF8String, includeOptimizerState);
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (BOOL)addIntPropertyWithName:(NSString*)name
value:(int64_t)value
error:(NSError**)error {
try {
[self CXXAPIOrtCheckpoint].AddProperty(name.UTF8String, value);
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (BOOL)addFloatPropertyWithName:(NSString*)name
value:(float)value
error:(NSError**)error {
try {
[self CXXAPIOrtCheckpoint].AddProperty(name.UTF8String, value);
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (BOOL)addStringPropertyWithName:(NSString*)name
value:(NSString*)value
error:(NSError**)error {
try {
[self CXXAPIOrtCheckpoint].AddProperty(name.UTF8String, value.UTF8String);
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (nullable NSString*)getStringPropertyWithName:(NSString*)name error:(NSError**)error {
try {
Ort::Property value = [self CXXAPIOrtCheckpoint].GetProperty(name.UTF8String);
if (std::string* str = std::get_if<std::string>(&value)) {
return [NSString stringWithUTF8String:str->c_str()];
}
ORT_CXX_API_THROW("Property is not a string.", ORT_INVALID_ARGUMENT);
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (int64_t)getIntPropertyWithName:(NSString*)name error:(NSError**)error {
try {
Ort::Property value = [self CXXAPIOrtCheckpoint].GetProperty(name.UTF8String);
if (int64_t* i = std::get_if<int64_t>(&value)) {
return *i;
}
ORT_CXX_API_THROW("Property is not an integer.", ORT_INVALID_ARGUMENT);
}
ORT_OBJC_API_IMPL_CATCH(error, 0)
}
- (float)getFloatPropertyWithName:(NSString*)name error:(NSError**)error {
try {
Ort::Property value = [self CXXAPIOrtCheckpoint].GetProperty(name.UTF8String);
if (float* f = std::get_if<float>(&value)) {
return *f;
}
ORT_CXX_API_THROW("Property is not a float.", ORT_INVALID_ARGUMENT);
}
ORT_OBJC_API_IMPL_CATCH(error, 0.0f)
}
- (Ort::CheckpointState&)CXXAPIOrtCheckpoint {
return *_checkpoint;
}
@end
NS_ASSUME_NONNULL_END
+16
View File
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ort_checkpoint.h"
#import "cxx_api.h"
NS_ASSUME_NONNULL_BEGIN
@interface ORTCheckpoint ()
- (Ort::CheckpointState&)CXXAPIOrtCheckpoint;
@end
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
+133
View File
@@ -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");
}
+17
View File
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ort_enums.h"
#import "cxx_api.h"
OrtLoggingLevel PublicToCAPILoggingLevel(ORTLoggingLevel logging_level);
ORTValueType CAPIToPublicValueType(ONNXType capi_type);
ONNXTensorElementDataType PublicToCAPITensorElementType(ORTTensorElementDataType type);
ORTTensorElementDataType CAPIToPublicTensorElementType(ONNXTensorElementDataType capi_type);
size_t SizeOfCAPITensorElementType(ONNXTensorElementDataType capi_type);
GraphOptimizationLevel PublicToCAPIGraphOptimizationLevel(ORTGraphOptimizationLevel opt_level);
+43
View File
@@ -0,0 +1,43 @@
// 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* _Nullable ORTVersion(void) {
return [NSString stringWithUTF8String:OrtGetApiBase()->GetVersionString()];
}
@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
+16
View File
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ort_env.h"
#import "cxx_api.h"
NS_ASSUME_NONNULL_BEGIN
@interface ORTEnv ()
- (Ort::Env&)CXXAPIOrtEnv;
@end
NS_ASSUME_NONNULL_END
+387
View File
@@ -0,0 +1,387 @@
// 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*> inputCAPIValues;
std::vector<OrtValue*> outputCAPIValues;
inputNames.reserve(inputs.count);
inputCAPIValues.reserve(inputs.count);
for (NSString* inputName in inputs) {
inputNames.push_back(inputName.UTF8String);
inputCAPIValues.push_back(static_cast<const OrtValue*>([inputs[inputName] CXXAPIOrtValue]));
}
outputNames.reserve(outputs.count);
outputCAPIValues.reserve(outputs.count);
for (NSString* outputName in outputs) {
outputNames.push_back(outputName.UTF8String);
outputCAPIValues.push_back(static_cast<OrtValue*>([outputs[outputName] CXXAPIOrtValue]));
}
Ort::ThrowOnError(Ort::GetApi().Run(*_session, [runOptions CXXAPIOrtRunOptions],
inputNames.data(), inputCAPIValues.data(), inputNames.size(),
outputNames.data(), outputNames.size(), outputCAPIValues.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*> inputCAPIValues;
std::vector<OrtValue*> outputCAPIValues;
inputNames.reserve(inputs.count);
inputCAPIValues.reserve(inputs.count);
for (NSString* inputName in inputs) {
inputNames.push_back(inputName.UTF8String);
inputCAPIValues.push_back(static_cast<const OrtValue*>([inputs[inputName] CXXAPIOrtValue]));
}
outputNames.reserve(outputNameArray.count);
outputCAPIValues.reserve(outputNameArray.count);
for (NSString* outputName in outputNameArray) {
outputNames.push_back(outputName.UTF8String);
outputCAPIValues.push_back(nullptr);
}
Ort::ThrowOnError(Ort::GetApi().Run(*_session, [runOptions CXXAPIOrtRunOptions],
inputNames.data(), inputCAPIValues.data(), inputNames.size(),
outputNames.data(), outputNames.size(), outputCAPIValues.data()));
NSMutableDictionary<NSString*, ORTValue*>* outputs = [[NSMutableDictionary alloc] init];
for (NSUInteger i = 0; i < outputNameArray.count; ++i) {
// Wrap the C OrtValue in a C++ Ort::Value to automatically handle its release.
// Then, transfer that C++ Ort::Value to a new ORTValue.
Ort::Value outputCXXAPIValue{outputCAPIValues[i]};
ORTValue* outputValue = [[ORTValue alloc] initWithCXXAPIOrtValue:std::move(outputCXXAPIValue)
externalTensorData:nil
error:error];
if (!outputValue) {
// clean up remaining C OrtValues which haven't been wrapped by a C++ Ort::Value yet
for (NSUInteger j = i + 1; j < outputNameArray.count; ++j) {
Ort::GetApi().ReleaseValue(outputCAPIValues[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)
}
- (BOOL)registerCustomOpsUsingFunctionPointer:(ORTCAPIRegisterCustomOpsFnPtr)registerCustomOpsFn
error:(NSError**)error {
try {
if (!registerCustomOpsFn) {
ORT_CXX_API_THROW("registerCustomOpsFn must not be null", ORT_INVALID_ARGUMENT);
}
Ort::ThrowOnError((*registerCustomOpsFn)(static_cast<OrtSessionOptions*>(*_sessionOptions),
OrtGetApiBase()));
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
+22
View File
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ort_session.h"
#import "cxx_api.h"
NS_ASSUME_NONNULL_BEGIN
@interface ORTSessionOptions ()
- (Ort::SessionOptions&)CXXAPIOrtSessionOptions;
@end
@interface ORTRunOptions ()
- (Ort::RunOptions&)CXXAPIOrtRunOptions;
@end
NS_ASSUME_NONNULL_END
+224
View File
@@ -0,0 +1,224 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ort_training_session_internal.h"
#import <vector>
#import <optional>
#import <string>
#import "cxx_api.h"
#import "cxx_utils.h"
#import "error_utils.h"
#import "ort_checkpoint_internal.h"
#import "ort_session_internal.h"
#import "ort_enums_internal.h"
#import "ort_env_internal.h"
#import "ort_value_internal.h"
NS_ASSUME_NONNULL_BEGIN
@implementation ORTTrainingSession {
std::optional<Ort::TrainingSession> _session;
ORTCheckpoint* _checkpoint;
}
- (Ort::TrainingSession&)CXXAPIOrtTrainingSession {
return *_session;
}
- (nullable instancetype)initWithEnv:(ORTEnv*)env
sessionOptions:(ORTSessionOptions*)sessionOptions
checkpoint:(ORTCheckpoint*)checkpoint
trainModelPath:(NSString*)trainModelPath
evalModelPath:(nullable NSString*)evalModelPath
optimizerModelPath:(nullable NSString*)optimizerModelPath
error:(NSError**)error {
if ((self = [super init]) == nil) {
return nil;
}
try {
std::optional<std::string> evalPath = utils::toStdOptionalString(evalModelPath);
std::optional<std::string> optimizerPath = utils::toStdOptionalString(optimizerModelPath);
_checkpoint = checkpoint;
_session = Ort::TrainingSession{
[env CXXAPIOrtEnv],
[sessionOptions CXXAPIOrtSessionOptions],
[checkpoint CXXAPIOrtCheckpoint],
trainModelPath.UTF8String,
evalPath,
optimizerPath};
return self;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (nullable NSArray<ORTValue*>*)trainStepWithInputValues:(NSArray<ORTValue*>*)inputs
error:(NSError**)error {
try {
std::vector<const OrtValue*> inputValues = utils::getWrappedCAPIOrtValues(inputs);
size_t outputCount;
Ort::ThrowOnError(Ort::GetTrainingApi().TrainingSessionGetTrainingModelOutputCount(*_session, &outputCount));
std::vector<OrtValue*> outputValues(outputCount, nullptr);
Ort::RunOptions runOptions;
Ort::ThrowOnError(Ort::GetTrainingApi().TrainStep(
*_session,
runOptions,
inputValues.size(),
inputValues.data(),
outputValues.size(),
outputValues.data()));
return utils::wrapUnownedCAPIOrtValues(outputValues, error);
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (nullable NSArray<ORTValue*>*)evalStepWithInputValues:(NSArray<ORTValue*>*)inputs
error:(NSError**)error {
try {
// create vector of OrtValue from NSArray<ORTValue*> with same size as inputValues
std::vector<const OrtValue*> inputValues = utils::getWrappedCAPIOrtValues(inputs);
size_t outputCount;
Ort::ThrowOnError(Ort::GetTrainingApi().TrainingSessionGetEvalModelOutputCount(*_session, &outputCount));
std::vector<OrtValue*> outputValues(outputCount, nullptr);
Ort::RunOptions runOptions;
Ort::ThrowOnError(Ort::GetTrainingApi().EvalStep(
*_session,
runOptions,
inputValues.size(),
inputValues.data(),
outputValues.size(),
outputValues.data()));
return utils::wrapUnownedCAPIOrtValues(outputValues, error);
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (BOOL)lazyResetGradWithError:(NSError**)error {
try {
[self CXXAPIOrtTrainingSession].LazyResetGrad();
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (BOOL)optimizerStepWithError:(NSError**)error {
try {
[self CXXAPIOrtTrainingSession].OptimizerStep();
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (nullable NSArray<NSString*>*)getTrainInputNamesWithError:(NSError**)error {
try {
std::vector<std::string> inputNames = [self CXXAPIOrtTrainingSession].InputNames(true);
return utils::toNSStringNSArray(inputNames);
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (nullable NSArray<NSString*>*)getTrainOutputNamesWithError:(NSError**)error {
try {
std::vector<std::string> outputNames = [self CXXAPIOrtTrainingSession].OutputNames(true);
return utils::toNSStringNSArray(outputNames);
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (nullable NSArray<NSString*>*)getEvalInputNamesWithError:(NSError**)error {
try {
std::vector<std::string> inputNames = [self CXXAPIOrtTrainingSession].InputNames(false);
return utils::toNSStringNSArray(inputNames);
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (nullable NSArray<NSString*>*)getEvalOutputNamesWithError:(NSError**)error {
try {
std::vector<std::string> outputNames = [self CXXAPIOrtTrainingSession].OutputNames(false);
return utils::toNSStringNSArray(outputNames);
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (BOOL)registerLinearLRSchedulerWithWarmupStepCount:(int64_t)warmupStepCount
totalStepCount:(int64_t)totalStepCount
initialLr:(float)initialLr
error:(NSError**)error {
try {
[self CXXAPIOrtTrainingSession].RegisterLinearLRScheduler(warmupStepCount, totalStepCount, initialLr);
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (BOOL)schedulerStepWithError:(NSError**)error {
try {
[self CXXAPIOrtTrainingSession].SchedulerStep();
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (float)getLearningRateWithError:(NSError**)error {
try {
return [self CXXAPIOrtTrainingSession].GetLearningRate();
}
ORT_OBJC_API_IMPL_CATCH(error, 0.0f);
}
- (BOOL)setLearningRate:(float)lr
error:(NSError**)error {
try {
[self CXXAPIOrtTrainingSession].SetLearningRate(lr);
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (BOOL)fromBufferWithValue:(ORTValue*)buffer
error:(NSError**)error {
try {
[self CXXAPIOrtTrainingSession].FromBuffer([buffer CXXAPIOrtValue]);
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
- (nullable ORTValue*)toBufferWithTrainable:(BOOL)onlyTrainable
error:(NSError**)error {
try {
Ort::Value val = [self CXXAPIOrtTrainingSession].ToBuffer(onlyTrainable);
return [[ORTValue alloc] initWithCXXAPIOrtValue:std::move(val)
externalTensorData:nil
error:error];
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error)
}
- (BOOL)exportModelForInferenceWithOutputPath:(NSString*)inferenceModelPath
graphOutputNames:(NSArray<NSString*>*)graphOutputNames
error:(NSError**)error {
try {
[self CXXAPIOrtTrainingSession].ExportModelForInferencing(utils::toStdString(inferenceModelPath),
utils::toStdStringVector(graphOutputNames));
return YES;
}
ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error)
}
@end
void ORTSetSeed(int64_t seed) {
Ort::SetSeed(seed);
}
NS_ASSUME_NONNULL_END
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ort_training_session.h"
#import "cxx_api.h"
NS_ASSUME_NONNULL_BEGIN
@interface ORTTrainingSession ()
- (Ort::TrainingSession&)CXXAPIOrtTrainingSession;
@end
NS_ASSUME_NONNULL_END
+162
View File
@@ -0,0 +1,162 @@
// 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 initWithCXXAPIOrtValue:std::move(ortValue)
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)initWithCXXAPIOrtValue:(Ort::Value&&)existingCXXAPIOrtValue
externalTensorData:(nullable NSMutableData*)externalTensorData
error:(NSError**)error {
if ((self = [super init]) == nil) {
return nil;
}
try {
_typeInfo = existingCXXAPIOrtValue.GetTypeInfo();
_externalTensorData = externalTensorData;
// transfer C++ Ort::Value ownership to this instance
_value = std::move(existingCXXAPIOrtValue);
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
+29
View File
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ort_value.h"
#import "cxx_api.h"
NS_ASSUME_NONNULL_BEGIN
@interface ORTValue ()
/**
* Creates a value from an existing C++ API Ort::Value and takes ownership from it.
* Note: Ownership is guaranteed to be transferred on success but not otherwise.
*
* @param existingCXXAPIOrtValue The existing C++ API Ort::Value.
* @param externalTensorData Any external tensor data referenced by `existingCXXAPIOrtValue`.
* @param error Optional error information set if an error occurs.
* @return The instance, or nil if an error occurs.
*/
- (nullable instancetype)initWithCXXAPIOrtValue:(Ort::Value&&)existingCXXAPIOrtValue
externalTensorData:(nullable NSMutableData*)externalTensorData
error:(NSError**)error NS_DESIGNATED_INITIALIZER;
- (Ort::Value&)CXXAPIOrtValue;
@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
+4
View File
@@ -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.");
+46
View File
@@ -0,0 +1,46 @@
// 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)
#define ORTAssertEqualFloatAndNoError(expected, result, error) \
do { \
XCTAssertEqualWithAccuracy(expected, result, 1e-3f, @"Expected %f but got %f. Error:%@", expected, result, error); \
XCTAssertNil(error); \
} while (0)
#define ORTAssertEqualFloatArrays(expected, result) \
do { \
XCTAssertEqual(expected.count, result.count); \
for (size_t i = 0; i < expected.count; ++i) { \
XCTAssertEqualWithAccuracy([expected[i] floatValue], [result[i] floatValue], 1e-3f); \
} \
} while (0)
NS_ASSUME_NONNULL_END
+117
View File
@@ -0,0 +1,117 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <XCTest/XCTest.h>
#import "ort_checkpoint.h"
#import "ort_training_session.h"
#import "ort_env.h"
#import "ort_session.h"
#import "test/test_utils.h"
#import "test/assertion_utils.h"
NS_ASSUME_NONNULL_BEGIN
@interface ORTCheckpointTest : XCTestCase
@property(readonly, nullable) ORTEnv* ortEnv;
@end
@implementation ORTCheckpointTest
- (void)setUp {
[super setUp];
self.continueAfterFailure = NO;
NSError* err = nil;
_ortEnv = [[ORTEnv alloc] initWithLoggingLevel:ORTLoggingLevelWarning
error:&err];
ORTAssertNullableResultSuccessful(_ortEnv, err);
}
+ (NSString*)getCheckpointPath {
NSBundle* bundle = [NSBundle bundleForClass:[ORTCheckpointTest class]];
NSString* path = [[bundle resourcePath] stringByAppendingPathComponent:@"checkpoint.ckpt"];
return path;
}
+ (NSString*)getTrainingModelPath {
NSBundle* bundle = [NSBundle bundleForClass:[ORTCheckpointTest class]];
NSString* path = [[bundle resourcePath] stringByAppendingPathComponent:@"training_model.onnx"];
return path;
}
- (void)testSaveCheckpoint {
NSError* error = nil;
ORTCheckpoint* checkpoint = [[ORTCheckpoint alloc] initWithPath:[ORTCheckpointTest getCheckpointPath] error:&error];
ORTAssertNullableResultSuccessful(checkpoint, error);
// save checkpoint
NSString* path = [test_utils::createTemporaryDirectory(self) stringByAppendingPathComponent:@"save_checkpoint.ckpt"];
XCTAssertNotNil(path);
BOOL result = [checkpoint saveCheckpointToPath:path withOptimizerState:NO error:&error];
ORTAssertBoolResultSuccessful(result, error);
}
- (void)testInitCheckpoint {
NSError* error = nil;
ORTCheckpoint* checkpoint = [[ORTCheckpoint alloc] initWithPath:[ORTCheckpointTest getCheckpointPath] error:&error];
ORTAssertNullableResultSuccessful(checkpoint, error);
}
- (void)testIntProperty {
NSError* error = nil;
// Load checkpoint
ORTCheckpoint* checkpoint = [[ORTCheckpoint alloc] initWithPath:[ORTCheckpointTest getCheckpointPath] error:&error];
ORTAssertNullableResultSuccessful(checkpoint, error);
// Add property
BOOL result = [checkpoint addIntPropertyWithName:@"test" value:314 error:&error];
ORTAssertBoolResultSuccessful(result, error);
// Get property
int64_t value = [checkpoint getIntPropertyWithName:@"test" error:&error];
XCTAssertEqual(value, 314);
}
- (void)testFloatProperty {
NSError* error = nil;
// Load checkpoint
ORTCheckpoint* checkpoint = [[ORTCheckpoint alloc] initWithPath:[ORTCheckpointTest getCheckpointPath] error:&error];
ORTAssertNullableResultSuccessful(checkpoint, error);
// Add property
BOOL result = [checkpoint addFloatPropertyWithName:@"test" value:3.14f error:&error];
ORTAssertBoolResultSuccessful(result, error);
// Get property
float value = [checkpoint getFloatPropertyWithName:@"test" error:&error];
XCTAssertEqual(value, 3.14f);
}
- (void)testStringProperty {
NSError* error = nil;
// Load checkpoint
ORTCheckpoint* checkpoint = [[ORTCheckpoint alloc] initWithPath:[ORTCheckpointTest getCheckpointPath] error:&error];
ORTAssertNullableResultSuccessful(checkpoint, error);
// Add property
BOOL result = [checkpoint addStringPropertyWithName:@"test" value:@"hello" error:&error];
ORTAssertBoolResultSuccessful(result, error);
// Get property
NSString* value = [checkpoint getStringPropertyWithName:@"test" error:&error];
XCTAssertEqualObjects(value, @"hello");
}
- (void)tearDown {
_ortEnv = nil;
[super tearDown];
}
@end
NS_ASSUME_NONNULL_END
+30
View File
@@ -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
+264
View File
@@ -0,0 +1,264 @@
// 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);
}
static bool gDummyRegisterCustomOpsFnCalled = false;
static OrtStatus* _Nullable DummyRegisterCustomOpsFn(OrtSessionOptions* /*session_options*/,
const OrtApiBase* /*api*/) {
gDummyRegisterCustomOpsFnCalled = true;
return nullptr;
}
- (void)testRegisterCustomOpsUsingFunctionPointer {
NSError* err = nil;
ORTSessionOptions* sessionOptions = [ORTSessionTest makeSessionOptions];
gDummyRegisterCustomOpsFnCalled = false;
BOOL registerResult = [sessionOptions registerCustomOpsUsingFunctionPointer:&DummyRegisterCustomOpsFn
error:&err];
ORTAssertBoolResultSuccessful(registerResult, err);
XCTAssertEqual(gDummyRegisterCustomOpsFnCalled, true);
}
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,359 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <XCTest/XCTest.h>
#import "ort_checkpoint.h"
#import "ort_training_session.h"
#import "ort_env.h"
#import "ort_session.h"
#import "ort_value.h"
#import "test/test_utils.h"
#import "test/assertion_utils.h"
NS_ASSUME_NONNULL_BEGIN
@interface ORTTrainingSessionTest : XCTestCase
@property(readonly, nullable) ORTEnv* ortEnv;
@property(readonly, nullable) ORTCheckpoint* checkpoint;
@property(readonly, nullable) ORTTrainingSession* session;
@end
@implementation ORTTrainingSessionTest
- (void)setUp {
[super setUp];
self.continueAfterFailure = NO;
NSError* err = nil;
_ortEnv = [[ORTEnv alloc] initWithLoggingLevel:ORTLoggingLevelWarning
error:&err];
ORTAssertNullableResultSuccessful(_ortEnv, err);
_checkpoint = [[ORTCheckpoint alloc] initWithPath:[ORTTrainingSessionTest
getFilePathFromName:@"checkpoint.ckpt"]
error:&err];
ORTAssertNullableResultSuccessful(_checkpoint, err);
_session = [self makeTrainingSessionWithCheckpoint:_checkpoint];
}
+ (NSString*)getFilePathFromName:(NSString*)name {
NSBundle* bundle = [NSBundle bundleForClass:[ORTTrainingSessionTest class]];
NSString* path = [[bundle resourcePath] stringByAppendingPathComponent:name];
return path;
}
+ (NSMutableData*)loadTensorDataFromFile:(NSString*)filePath skipHeader:(BOOL)skipHeader {
NSError* error = nil;
NSString* fileContents = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:&error];
ORTAssertNullableResultSuccessful(fileContents, error);
NSArray<NSString*>* lines = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
if (skipHeader) {
lines = [lines subarrayWithRange:NSMakeRange(1, lines.count - 1)];
}
NSArray<NSString*>* dataArray = [lines[0] componentsSeparatedByCharactersInSet:
[NSCharacterSet characterSetWithCharactersInString:@",[] "]];
NSMutableData* tensorData = [NSMutableData data];
for (NSString* str in dataArray) {
if (str.length > 0) {
float value = [str floatValue];
[tensorData appendBytes:&value length:sizeof(float)];
}
}
return tensorData;
}
- (ORTTrainingSession*)makeTrainingSessionWithCheckpoint:(ORTCheckpoint*)checkpoint {
NSError* error = nil;
ORTSessionOptions* sessionOptions = [[ORTSessionOptions alloc] initWithError:&error];
ORTAssertNullableResultSuccessful(sessionOptions, error);
ORTTrainingSession* session = [[ORTTrainingSession alloc]
initWithEnv:self.ortEnv
sessionOptions:sessionOptions
checkpoint:checkpoint
trainModelPath:[ORTTrainingSessionTest getFilePathFromName:@"training_model.onnx"]
evalModelPath:[ORTTrainingSessionTest getFilePathFromName:@"eval_model.onnx"]
optimizerModelPath:[ORTTrainingSessionTest getFilePathFromName:@"adamw.onnx"]
error:&error];
ORTAssertNullableResultSuccessful(session, error);
return session;
}
- (void)testInitTrainingSession {
NSError* error = nil;
// check that inputNames contains input-0
NSArray<NSString*>* inputNames = [self.session getTrainInputNamesWithError:&error];
ORTAssertNullableResultSuccessful(inputNames, error);
XCTAssertTrue(inputNames.count > 0);
XCTAssertTrue([inputNames containsObject:@"input-0"]);
// check that outNames contains onnx::loss::21273
NSArray<NSString*>* outputNames = [self.session getTrainOutputNamesWithError:&error];
ORTAssertNullableResultSuccessful(outputNames, error);
XCTAssertTrue(outputNames.count > 0);
XCTAssertTrue([outputNames containsObject:@"onnx::loss::21273"]);
}
- (void)testInitTrainingSessionWithEval {
NSError* error = nil;
// check that inputNames contains input-0
NSArray<NSString*>* inputNames = [self.session getEvalInputNamesWithError:&error];
ORTAssertNullableResultSuccessful(inputNames, error);
XCTAssertTrue(inputNames.count > 0);
XCTAssertTrue([inputNames containsObject:@"input-0"]);
// check that outNames contains onnx::loss::21273
NSArray<NSString*>* outputNames = [self.session getEvalOutputNamesWithError:&error];
ORTAssertNullableResultSuccessful(outputNames, error);
XCTAssertTrue(outputNames.count > 0);
XCTAssertTrue([outputNames containsObject:@"onnx::loss::21273"]);
}
- (void)runTrainStep {
// load input and expected output
NSError* error = nil;
NSMutableData* expectedOutput = [ORTTrainingSessionTest loadTensorDataFromFile:[ORTTrainingSessionTest
getFilePathFromName:@"loss_1.out"]
skipHeader:YES];
NSMutableData* input = [ORTTrainingSessionTest loadTensorDataFromFile:[ORTTrainingSessionTest
getFilePathFromName:@"input-0.in"]
skipHeader:YES];
int32_t labels[] = {1, 1};
// create ORTValue array for input and labels
NSMutableArray<ORTValue*>* inputValues = [NSMutableArray array];
ORTValue* inputTensor = [[ORTValue alloc] initWithTensorData:input
elementType:ORTTensorElementDataTypeFloat
shape:@[ @2, @784 ]
error:&error];
ORTAssertNullableResultSuccessful(inputTensor, error);
[inputValues addObject:inputTensor];
ORTValue* labelTensor = [[ORTValue alloc] initWithTensorData:[NSMutableData dataWithBytes:labels
length:sizeof(labels)]
elementType:ORTTensorElementDataTypeInt32
shape:@[ @2 ]
error:&error];
ORTAssertNullableResultSuccessful(labelTensor, error);
[inputValues addObject:labelTensor];
NSArray<ORTValue*>* outputs = [self.session trainStepWithInputValues:inputValues error:&error];
ORTAssertNullableResultSuccessful(outputs, error);
XCTAssertTrue(outputs.count > 0);
BOOL result = [self.session lazyResetGradWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
outputs = [self.session trainStepWithInputValues:inputValues error:&error];
ORTAssertNullableResultSuccessful(outputs, error);
XCTAssertTrue(outputs.count > 0);
ORTValue* outputValue = outputs[0];
ORTValueTypeInfo* typeInfo = [outputValue typeInfoWithError:&error];
ORTAssertNullableResultSuccessful(typeInfo, error);
XCTAssertEqual(typeInfo.type, ORTValueTypeTensor);
XCTAssertNotNil(typeInfo.tensorTypeAndShapeInfo);
ORTTensorTypeAndShapeInfo* tensorInfo = [outputValue tensorTypeAndShapeInfoWithError:&error];
ORTAssertNullableResultSuccessful(tensorInfo, error);
XCTAssertEqual(tensorInfo.elementType, ORTTensorElementDataTypeFloat);
NSMutableData* tensorData = [outputValue tensorDataWithError:&error];
ORTAssertNullableResultSuccessful(tensorData, error);
ORTAssertEqualFloatArrays(test_utils::getFloatArrayFromData(tensorData),
test_utils::getFloatArrayFromData(expectedOutput));
}
- (void)testTrainStepOutput {
[self runTrainStep];
}
- (void)testOptimizerStep {
// load input and expected output
NSError* error = nil;
NSMutableData* expectedOutput1 = [ORTTrainingSessionTest loadTensorDataFromFile:[ORTTrainingSessionTest
getFilePathFromName:@"loss_1.out"]
skipHeader:YES];
NSMutableData* expectedOutput2 = [ORTTrainingSessionTest loadTensorDataFromFile:[ORTTrainingSessionTest
getFilePathFromName:@"loss_2.out"]
skipHeader:YES];
NSMutableData* input = [ORTTrainingSessionTest loadTensorDataFromFile:[ORTTrainingSessionTest
getFilePathFromName:@"input-0.in"]
skipHeader:YES];
int32_t labels[] = {1, 1};
// create ORTValue array for input and labels
NSMutableArray<ORTValue*>* inputValues = [NSMutableArray array];
ORTValue* inputTensor = [[ORTValue alloc] initWithTensorData:input
elementType:ORTTensorElementDataTypeFloat
shape:@[ @2, @784 ]
error:&error];
ORTAssertNullableResultSuccessful(inputTensor, error);
[inputValues addObject:inputTensor];
ORTValue* labelTensor = [[ORTValue alloc] initWithTensorData:[NSMutableData dataWithBytes:labels
length:sizeof(labels)]
elementType:ORTTensorElementDataTypeInt32
shape:@[ @2 ]
error:&error];
ORTAssertNullableResultSuccessful(labelTensor, error);
[inputValues addObject:labelTensor];
// run train step, optimizer steps and check loss
NSArray<ORTValue*>* outputs = [self.session trainStepWithInputValues:inputValues error:&error];
ORTAssertNullableResultSuccessful(outputs, error);
NSMutableData* loss = [outputs[0] tensorDataWithError:&error];
ORTAssertNullableResultSuccessful(loss, error);
ORTAssertEqualFloatArrays(test_utils::getFloatArrayFromData(loss),
test_utils::getFloatArrayFromData(expectedOutput1));
BOOL result = [self.session lazyResetGradWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
outputs = [self.session trainStepWithInputValues:inputValues error:&error];
ORTAssertNullableResultSuccessful(outputs, error);
loss = [outputs[0] tensorDataWithError:&error];
ORTAssertNullableResultSuccessful(loss, error);
ORTAssertEqualFloatArrays(test_utils::getFloatArrayFromData(loss),
test_utils::getFloatArrayFromData(expectedOutput1));
result = [self.session optimizerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
outputs = [self.session trainStepWithInputValues:inputValues error:&error];
ORTAssertNullableResultSuccessful(outputs, error);
loss = [outputs[0] tensorDataWithError:&error];
ORTAssertNullableResultSuccessful(loss, error);
ORTAssertEqualFloatArrays(test_utils::getFloatArrayFromData(loss),
test_utils::getFloatArrayFromData(expectedOutput2));
}
- (void)testSetLearningRate {
NSError* error = nil;
float learningRate = 0.1f;
BOOL result = [self.session setLearningRate:learningRate error:&error];
ORTAssertBoolResultSuccessful(result, error);
float actualLearningRate = [self.session getLearningRateWithError:&error];
ORTAssertEqualFloatAndNoError(learningRate, actualLearningRate, error);
}
- (void)testLinearLRScheduler {
NSError* error = nil;
float learningRate = 0.1f;
BOOL result = [self.session registerLinearLRSchedulerWithWarmupStepCount:2
totalStepCount:4
initialLr:learningRate
error:&error];
ORTAssertBoolResultSuccessful(result, error);
[self runTrainStep];
result = [self.session optimizerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
result = [self.session schedulerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
ORTAssertEqualFloatAndNoError(0.05f, [self.session getLearningRateWithError:&error], error);
result = [self.session optimizerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
result = [self.session schedulerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
ORTAssertEqualFloatAndNoError(0.1f, [self.session getLearningRateWithError:&error], error);
result = [self.session optimizerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
result = [self.session schedulerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
ORTAssertEqualFloatAndNoError(0.05f, [self.session getLearningRateWithError:&error], error);
result = [self.session optimizerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
result = [self.session schedulerStepWithError:&error];
ORTAssertBoolResultSuccessful(result, error);
ORTAssertEqualFloatAndNoError(0.0f, [self.session getLearningRateWithError:&error], error);
}
- (void)testExportModelForInference {
NSError* error = nil;
NSString* inferenceModelPath = [test_utils::createTemporaryDirectory(self)
stringByAppendingPathComponent:@"inference_model.onnx"];
XCTAssertNotNil(inferenceModelPath);
NSArray<NSString*>* graphOutputNames = [NSArray arrayWithObjects:@"output-0", nil];
BOOL result = [self.session exportModelForInferenceWithOutputPath:inferenceModelPath
graphOutputNames:graphOutputNames
error:&error];
ORTAssertBoolResultSuccessful(result, error);
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:inferenceModelPath]);
[self addTeardownBlock:^{
NSError* error = nil;
[[NSFileManager defaultManager] removeItemAtPath:inferenceModelPath error:&error];
}];
}
- (void)testToBuffer {
NSError* error = nil;
ORTValue* buffer = [self.session toBufferWithTrainable:YES error:&error];
ORTAssertNullableResultSuccessful(buffer, error);
ORTValueTypeInfo* typeInfo = [buffer typeInfoWithError:&error];
ORTAssertNullableResultSuccessful(typeInfo, error);
XCTAssertEqual(typeInfo.type, ORTValueTypeTensor);
XCTAssertNotNil(typeInfo.tensorTypeAndShapeInfo);
}
- (void)testFromBuffer {
NSError* error = nil;
ORTValue* buffer = [self.session toBufferWithTrainable:YES error:&error];
ORTAssertNullableResultSuccessful(buffer, error);
BOOL result = [self.session fromBufferWithValue:buffer error:&error];
ORTAssertBoolResultSuccessful(result, error);
}
- (void)tearDown {
_session = nil;
_checkpoint = nil;
_ortEnv = nil;
[super tearDown];
}
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <XCTest/XCTest.h>
#import "ort_training_session.h"
NS_ASSUME_NONNULL_BEGIN
@interface ORTTrainingUtilsTest : XCTestCase
@end
@implementation ORTTrainingUtilsTest
- (void)setUp {
[super setUp];
self.continueAfterFailure = NO;
}
- (void)testSetSeed {
ORTSetSeed(2718);
}
@end
NS_ASSUME_NONNULL_END
+79
View File
@@ -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
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import "ort_session.h"
#import "ort_env.h"
#import "ort_value.h"
NS_ASSUME_NONNULL_BEGIN
namespace test_utils {
NSString* _Nullable createTemporaryDirectory(XCTestCase* testCase);
NSArray<NSNumber*>* getFloatArrayFromData(NSData* data);
} // namespace test_utils
NS_ASSUME_NONNULL_END
+44
View File
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "test_utils.h"
NS_ASSUME_NONNULL_BEGIN
namespace test_utils {
NSString* createTemporaryDirectory(XCTestCase* testCase) {
NSString* temporaryDirectory = NSTemporaryDirectory();
NSString* directoryPath = [temporaryDirectory stringByAppendingPathComponent:@"ort-objective-c-test"];
NSError* error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
withIntermediateDirectories:YES
attributes:nil
error:&error];
XCTAssertNil(error, @"Error creating temporary directory: %@", error.localizedDescription);
// add teardown block to delete the temporary directory
[testCase addTeardownBlock:^{
NSError* error = nil;
[[NSFileManager defaultManager] removeItemAtPath:directoryPath error:&error];
XCTAssertNil(error, @"Error removing temporary directory: %@", error.localizedDescription);
}];
return directoryPath;
}
NSArray<NSNumber*>* getFloatArrayFromData(NSData* data) {
NSMutableArray<NSNumber*>* array = [NSMutableArray array];
float value;
for (size_t i = 0; i < data.length / sizeof(float); ++i) {
[data getBytes:&value range:NSMakeRange(i * sizeof(float), sizeof(float))];
[array addObject:[NSNumber numberWithFloat:value]];
}
return array;
}
} // namespace test_utils
NS_ASSUME_NONNULL_END
+12
View File
@@ -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 .
Binary file not shown.
Binary file not shown.
+19
View File
@@ -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")