From 5d78f490a6c415d40e47c96e0444b3c6131a6c9f Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:38:56 -0800 Subject: [PATCH 1/6] Copy objectivec/ from ORT v1.20. --- objectivec/include/ort_coreml_execution_provider.h | 5 ++++- objectivec/ort_coreml_execution_provider.mm | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/objectivec/include/ort_coreml_execution_provider.h b/objectivec/include/ort_coreml_execution_provider.h index 6ff1817..d7d873f 100644 --- a/objectivec/include/ort_coreml_execution_provider.h +++ b/objectivec/include/ort_coreml_execution_provider.h @@ -29,7 +29,10 @@ NS_ASSUME_NONNULL_BEGIN * Whether the CoreML execution provider should run on CPU only. */ @property BOOL useCPUOnly; - +/** + * exclude ANE in CoreML. + */ +@property BOOL useCPUAndGPU; /** * Whether the CoreML execution provider is enabled on subgraphs. */ diff --git a/objectivec/ort_coreml_execution_provider.mm b/objectivec/ort_coreml_execution_provider.mm index 58b47d6..6cb5026 100644 --- a/objectivec/ort_coreml_execution_provider.mm +++ b/objectivec/ort_coreml_execution_provider.mm @@ -25,6 +25,7 @@ BOOL ORTIsCoreMLExecutionProviderAvailable() { try { const uint32_t flags = (options.useCPUOnly ? COREML_FLAG_USE_CPU_ONLY : 0) | + (options.useCPUAndGPU ? COREML_FLAG_USE_CPU_AND_GPU : 0) | (options.enableOnSubgraphs ? COREML_FLAG_ENABLE_ON_SUBGRAPH : 0) | (options.onlyEnableForDevicesWithANE ? COREML_FLAG_ONLY_ENABLE_DEVICE_WITH_ANE : 0) | (options.onlyAllowStaticInputShapes ? COREML_FLAG_ONLY_ALLOW_STATIC_INPUT_SHAPES : 0) | From f2910f23d62cfe89812dcd8842930b6c0947254f Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:44:35 -0800 Subject: [PATCH 2/6] update ORT and ORT download URLs and checksums --- Package.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index d766de2..e240641 100644 --- a/Package.swift +++ b/Package.swift @@ -98,9 +98,9 @@ if let pod_archive_path = ProcessInfo.processInfo.environment["ORT_POD_LOCAL_PAT // ORT release package.targets.append( Target.binaryTarget(name: "onnxruntime", - url: "https://download.onnxruntime.ai/pod-archive-onnxruntime-c-1.19.2.zip", + url: "https://download.onnxruntime.ai/pod-archive-onnxruntime-c-1.20.0.zip", // SHA256 checksum - checksum: "28787ee2f966a2c47eb293322c733c5dc4b5e3327cec321c1fe31a7c698edf68") + checksum: "50891a8aadd17d4811acb05ed151ba6c394129bb3ab14e843b0fc83a48d450ff") ) } @@ -110,8 +110,8 @@ if let ext_pod_archive_path = ProcessInfo.processInfo.environment["ORT_EXTENSION // ORT Extensions release package.targets.append( Target.binaryTarget(name: "onnxruntime_extensions", - url: "https://download.onnxruntime.ai/pod-archive-onnxruntime-extensions-c-0.12.0.zip", + url: "https://download.onnxruntime.ai/pod-archive-onnxruntime-extensions-c-0.13.0.zip", // SHA256 checksum - checksum: "542be5904cf2cc93db6b8afcd11b023b001333e3151bea8bff1c7b1cf8959fab") + checksum: "346522d1171d4c99cb0908fa8e4e9330a4a6aad39cd83ce36eb654437b33e6b5") ) } From 5b8bc9df5916665f67dc6c15c9725aeb6f64f925 Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:30:32 -0800 Subject: [PATCH 3/6] remove nonexistent exclude path 'test' --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index e240641..35b71c4 100644 --- a/Package.swift +++ b/Package.swift @@ -36,7 +36,7 @@ let package = Package( .target(name: "OnnxRuntimeBindings", dependencies: ["onnxruntime"], path: "objectivec", - exclude: ["ReadMe.md", "format_objc.sh", "test", + exclude: ["ReadMe.md", "format_objc.sh", "ort_checkpoint.mm", "ort_checkpoint_internal.h", "ort_training_session_internal.h", From 149f4a677e7e6aba9732cab37f002c9c0c4aa37a Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:53:17 -0800 Subject: [PATCH 4/6] Revert "remove nonexistent exclude path 'test'" This reverts commit 5b8bc9df5916665f67dc6c15c9725aeb6f64f925. --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 35b71c4..e240641 100644 --- a/Package.swift +++ b/Package.swift @@ -36,7 +36,7 @@ let package = Package( .target(name: "OnnxRuntimeBindings", dependencies: ["onnxruntime"], path: "objectivec", - exclude: ["ReadMe.md", "format_objc.sh", + exclude: ["ReadMe.md", "format_objc.sh", "test", "ort_checkpoint.mm", "ort_checkpoint_internal.h", "ort_training_session_internal.h", From 93228a1552eb5d4b462c9ac3e10acbf19501d8ec Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:58:49 -0800 Subject: [PATCH 5/6] add objectivec/test and objectivec/docs --- objectivec/docs/jazzy_config.yaml | 15 + objectivec/docs/main_page.md | 5 + objectivec/docs/readme.md | 17 + objectivec/test/assert_arc_enabled.mm | 4 + objectivec/test/assertion_utils.h | 46 +++ objectivec/test/ort_checkpoint_test.mm | 117 ++++++ objectivec/test/ort_env_test.mm | 30 ++ objectivec/test/ort_session_test.mm | 326 ++++++++++++++++ objectivec/test/ort_training_session_test.mm | 359 ++++++++++++++++++ objectivec/test/ort_training_utils_test.mm | 26 ++ objectivec/test/ort_value_test.mm | 92 +++++ objectivec/test/test_utils.h | 21 + objectivec/test/test_utils.mm | 44 +++ objectivec/test/testdata/gen_models.sh | 12 + objectivec/test/testdata/identity_string.ort | Bin 0 -> 1288 bytes objectivec/test/testdata/single_add.basic.ort | Bin 0 -> 1296 bytes objectivec/test/testdata/single_add.onnx | Bin 0 -> 93 bytes objectivec/test/testdata/single_add_gen.py | 19 + 18 files changed, 1133 insertions(+) create mode 100644 objectivec/docs/jazzy_config.yaml create mode 100644 objectivec/docs/main_page.md create mode 100644 objectivec/docs/readme.md create mode 100644 objectivec/test/assert_arc_enabled.mm create mode 100644 objectivec/test/assertion_utils.h create mode 100644 objectivec/test/ort_checkpoint_test.mm create mode 100644 objectivec/test/ort_env_test.mm create mode 100644 objectivec/test/ort_session_test.mm create mode 100644 objectivec/test/ort_training_session_test.mm create mode 100644 objectivec/test/ort_training_utils_test.mm create mode 100644 objectivec/test/ort_value_test.mm create mode 100644 objectivec/test/test_utils.h create mode 100644 objectivec/test/test_utils.mm create mode 100644 objectivec/test/testdata/gen_models.sh create mode 100644 objectivec/test/testdata/identity_string.ort create mode 100644 objectivec/test/testdata/single_add.basic.ort create mode 100644 objectivec/test/testdata/single_add.onnx create mode 100644 objectivec/test/testdata/single_add_gen.py diff --git a/objectivec/docs/jazzy_config.yaml b/objectivec/docs/jazzy_config.yaml new file mode 100644 index 0000000..700bb9b --- /dev/null +++ b/objectivec/docs/jazzy_config.yaml @@ -0,0 +1,15 @@ +module: ONNX Runtime Objective-C API +author: ONNX Runtime Authors +author_url: https://www.onnxruntime.ai +github_url: https://github.com/microsoft/onnxruntime + +objc: true +# Specify the training header as umbrella_header so that every public header is included. +# The training header is a superset of the inference-only header. +umbrella_header: ../include/onnxruntime_training.h +framework_root: .. + +readme: ./main_page.md + +hide_documentation_coverage: true +undocumented_text: "" diff --git a/objectivec/docs/main_page.md b/objectivec/docs/main_page.md new file mode 100644 index 0000000..fdc7c80 --- /dev/null +++ b/objectivec/docs/main_page.md @@ -0,0 +1,5 @@ +# ONNX Runtime Objective-C API + +ONNX Runtime provides an Objective-C API. + +It can be used from Objective-C/C++ or Swift with a bridging header. diff --git a/objectivec/docs/readme.md b/objectivec/docs/readme.md new file mode 100644 index 0000000..5697480 --- /dev/null +++ b/objectivec/docs/readme.md @@ -0,0 +1,17 @@ +# Objective-C API Documentation + +The API should be documented with comments in the [public header files](../include). + +## Documentation Generation + +The [Jazzy](https://github.com/realm/jazzy) tool is used to generate documentation from the code. + +To generate documentation, from the repo root, run: + +```bash +jazzy --config objectivec/docs/jazzy_config.yaml --output +``` + +The generated documentation website files will be in ``. + +[main_page.md](./main_page.md) contains content for the main page of the generated documentation website. diff --git a/objectivec/test/assert_arc_enabled.mm b/objectivec/test/assert_arc_enabled.mm new file mode 100644 index 0000000..9aa0bad --- /dev/null +++ b/objectivec/test/assert_arc_enabled.mm @@ -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."); diff --git a/objectivec/test/assertion_utils.h b/objectivec/test/assertion_utils.h new file mode 100644 index 0000000..2b72435 --- /dev/null +++ b/objectivec/test/assertion_utils.h @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +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 diff --git a/objectivec/test/ort_checkpoint_test.mm b/objectivec/test/ort_checkpoint_test.mm new file mode 100644 index 0000000..9b2196f --- /dev/null +++ b/objectivec/test/ort_checkpoint_test.mm @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#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 diff --git a/objectivec/test/ort_env_test.mm b/objectivec/test/ort_env_test.mm new file mode 100644 index 0000000..420f55f --- /dev/null +++ b/objectivec/test/ort_env_test.mm @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#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 diff --git a/objectivec/test/ort_session_test.mm b/objectivec/test/ort_session_test.mm new file mode 100644 index 0000000..508289f --- /dev/null +++ b/objectivec/test/ort_session_test.mm @@ -0,0 +1,326 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#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 + +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; +} + ++ (NSString*)getStringModelPath { + NSBundle* bundle = [NSBundle bundleForClass:[ORTSessionTest class]]; + NSString* path = [bundle pathForResource:@"identity_string" + ofType:@"ort"]; + return path; +} + ++ (NSMutableData*)dataWithScalarFloat:(float)value { + NSMutableData* data = [[NSMutableData alloc] initWithBytes:&value length:sizeof(value)]; + return data; +} + ++ (ORTValue*)ortValueWithScalarFloatData:(NSMutableData*)data { + NSArray* 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* 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* inputNames = [session inputNamesWithError:&err]; + ORTAssertNullableResultSuccessful(inputNames, err); + XCTAssertEqualObjects(inputNames, (@[ @"A", @"B" ])); + + NSArray* overridableInitializerNames = [session overridableInitializerNamesWithError:&err]; + ORTAssertNullableResultSuccessful(overridableInitializerNames, err); + XCTAssertEqualObjects(overridableInitializerNames, (@[])); + + NSArray* 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); +} + +- (void)testStringInputs { + NSError* err = nil; + NSArray* stringData = @[ @"ONNX Runtime", @"is the", @"best", @"AI Framework" ]; + ORTValue* stringValue = [[ORTValue alloc] initWithTensorStringData:stringData shape:@[ @2, @2 ] error:&err]; + ORTAssertNullableResultSuccessful(stringValue, err); + + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:[ORTSessionTest getStringModelPath] + sessionOptions:[ORTSessionTest makeSessionOptions] + error:&err]; + ORTAssertNullableResultSuccessful(session, err); + + NSDictionary* outputs = + [session runWithInputs:@{@"input:0" : stringValue} + outputNames:[NSSet setWithArray:@[ @"output:0" ]] + runOptions:[ORTSessionTest makeRunOptions] + error:&err]; + ORTAssertNullableResultSuccessful(outputs, err); + + ORTValue* outputStringValue = outputs[@"output:0"]; + XCTAssertNotNil(outputStringValue); + + NSArray* outputStringData = [outputStringValue tensorStringDataWithError:&err]; + ORTAssertNullableResultSuccessful(outputStringData, err); + + XCTAssertEqual([stringData count], [outputStringData count]); + XCTAssertTrue([stringData isEqualToArray:outputStringData]); +} + +- (void)testKeepORTEnvReference { + ORTEnv* __weak envWeak = _ortEnv; + // Remove sole strong reference to the ORTEnv created in setUp. + _ortEnv = nil; + // There should be no more strong references to it. + XCTAssertNil(envWeak); + + // Create a new ORTEnv. + NSError* err = nil; + ORTEnv* env = [[ORTEnv alloc] initWithLoggingLevel:ORTLoggingLevelWarning + error:&err]; + ORTAssertNullableResultSuccessful(env, err); + + ORTSession* session = [[ORTSession alloc] initWithEnv:env + modelPath:[ORTSessionTest getAddModelPath] + sessionOptions:[ORTSessionTest makeSessionOptions] + error:&err]; + ORTAssertNullableResultSuccessful(session, err); + + envWeak = env; + // Remove strong reference to the ORTEnv passed to the ORTSession initializer. + env = nil; + // ORTSession should keep a strong reference to it. + XCTAssertNotNil(envWeak); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/objectivec/test/ort_training_session_test.mm b/objectivec/test/ort_training_session_test.mm new file mode 100644 index 0000000..683965d --- /dev/null +++ b/objectivec/test/ort_training_session_test.mm @@ -0,0 +1,359 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#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* lines = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + + if (skipHeader) { + lines = [lines subarrayWithRange:NSMakeRange(1, lines.count - 1)]; + } + + NSArray* 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* 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* 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* 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* 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* 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* 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* 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* 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* 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 diff --git a/objectivec/test/ort_training_utils_test.mm b/objectivec/test/ort_training_utils_test.mm new file mode 100644 index 0000000..1695ddc --- /dev/null +++ b/objectivec/test/ort_training_utils_test.mm @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import +#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 diff --git a/objectivec/test/ort_value_test.mm b/objectivec/test/ort_value_test.mm new file mode 100644 index 0000000..b22d73b --- /dev/null +++ b/objectivec/test/ort_value_test.mm @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#import "ort_value.h" + +#include + +#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* 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 values{1, 2, 3, 4}; + NSMutableData* data = [[NSMutableData alloc] initWithBytes:values.data() + length:values.size() * sizeof(int32_t)]; + NSArray* shape = @[ @2, @3 ]; // too large + + NSError* err = nil; + ORTValue* ortValue = [[ORTValue alloc] initWithTensorData:data + elementType:ORTTensorElementDataTypeInt32 + shape:shape + error:&err]; + ORTAssertNullableResultUnsuccessful(ortValue, err); +} + +- (void)testInitTensorWithStringDataSucceeds { + NSArray* stringData = @[ @"ONNX Runtime", @"is", @"the", @"best", @"AI", @"Framework" ]; + NSError* err = nil; + ORTValue* stringValue = [[ORTValue alloc] initWithTensorStringData:stringData shape:@[ @3, @2 ] error:&err]; + ORTAssertNullableResultSuccessful(stringValue, err); + + NSArray* returnedStringData = [stringValue tensorStringDataWithError:&err]; + ORTAssertNullableResultSuccessful(returnedStringData, err); + + XCTAssertEqual([stringData count], [returnedStringData count]); + XCTAssertTrue([stringData isEqualToArray:returnedStringData]); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/objectivec/test/test_utils.h b/objectivec/test/test_utils.h new file mode 100644 index 0000000..8a5e6e4 --- /dev/null +++ b/objectivec/test/test_utils.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import +#import + +#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* getFloatArrayFromData(NSData* data); + +} // namespace test_utils + +NS_ASSUME_NONNULL_END diff --git a/objectivec/test/test_utils.mm b/objectivec/test/test_utils.mm new file mode 100644 index 0000000..ef8e8c5 --- /dev/null +++ b/objectivec/test/test_utils.mm @@ -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* getFloatArrayFromData(NSData* data) { + NSMutableArray* 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 diff --git a/objectivec/test/testdata/gen_models.sh b/objectivec/test/testdata/gen_models.sh new file mode 100644 index 0000000..0a58ca8 --- /dev/null +++ b/objectivec/test/testdata/gen_models.sh @@ -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 . diff --git a/objectivec/test/testdata/identity_string.ort b/objectivec/test/testdata/identity_string.ort new file mode 100644 index 0000000000000000000000000000000000000000..96ba0f37c69490e973e26263853a16c3300a8b58 GIT binary patch literal 1288 zcmZux&1w@-6h5Pa9os5FLn$Jqi!8Dzmsl#4;HoOeE^p}f@^W%6NpP6p&O0gcki882M>I6e(w3Z=iHWj*=htioCXqW8IfLE^GHp#@3Ftl^Z`&209<5oUVjmq@5LR2&zf}5ZQIoobjm|`qz&^rOGz_-A+fgZqm12o+S ze+g7Xdxh5!D1X3*J>Ty=e>lyviPeSamBpbR-vRf@9r(L3 zD#k5+d>b|1W45${qPIcDcx4ANImRUG0Oc3K)&qwc@r-1G{j_Fh&FkTPu_o_x#(*&A zrO=D-Poe}9=kD2$?^NVV$jw@uE&KQm8+Vh)4ZJAt4|U#;xVOd^=69H20{{1e1Bmlx zUe@%p>$23`z5rPpj1=apkl!KT5zqq8p??KVfmcA)NZB&gMw>mC7c*k6 zir53^?{bfoHa0I!dY}xGgA(_SIemX`+DMGM2K>_#^b_Q-->>TDVjrL2SN%?zi|?TF z8y?{PwLG`#wNySG<)t3tUnYe=cj;e=V5goqTjsX;7_a^>@w}rtYEyNbuif3`yxd;z G;_MH{>baQ! literal 0 HcmV?d00001 diff --git a/objectivec/test/testdata/single_add.basic.ort b/objectivec/test/testdata/single_add.basic.ort new file mode 100644 index 0000000000000000000000000000000000000000..f622784b35366ea8f0c33ccbdb513b5d6f7df7c1 GIT binary patch literal 1296 zcmaJ=L2DCH5T59w+gK&qP=ZMC5X6H#a!@S6gSGW228qz?vUxTO&D*eD(+Gkdq9fHT2w9Z|5JC$eX^YH( zlR%{XYq5Zk8bCRK-6(YKQNIB6HiyIBeUTjJWFiO82f%rt3)}>_&xlYiY8;GOSmb7$ z&l6~_CL&tWWB~H``e>1B;n$lD6g595VDqaf9Yo#GIDe}^8Mfg=^Z{}(59JBa!5-}v zK)DTw)V0wS&J3ry_RPrw0VxVu)->>nX{L6ljZwugT zn6K>BBj`Na1Xh4|F#13TU_ZV9N5By10*w3i+xH!++T=^KT{C%=r&&BQQEna2_~=2S zi*eV1I31gD5@+dt`XV=WHnCYUwl0tMa9HtY5MwR_r($65RQ%|}{~$_C>fFpsGCNJ| zfyuKdaf#c-k-R_S&w5M$s^44qU1dPA$jzB){(Y0U+-5G?Gj4Pc)8#w-`Bu5;%UPDZ nW85dyzl#&~J%&)fHONzUGA<@%sQ;t)_p@e%kMWx4xd-D9DJ`oI literal 0 HcmV?d00001 diff --git a/objectivec/test/testdata/single_add.onnx b/objectivec/test/testdata/single_add.onnx new file mode 100644 index 0000000000000000000000000000000000000000..82e8fe669e0d84b6111f678b0742a552d820269b GIT binary patch literal 93 zcmd;Jw+iMG=3;c@VssK>be3XvOi57!5kj27nR)3ssX%5FKTuwXi;IJUQHX_$iGvX& U;DjuY1Qc*a7I0$WVi4c~0E&7GKL7v# literal 0 HcmV?d00001 diff --git a/objectivec/test/testdata/single_add_gen.py b/objectivec/test/testdata/single_add_gen.py new file mode 100644 index 0000000..15b4372 --- /dev/null +++ b/objectivec/test/testdata/single_add_gen.py @@ -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") From f3a18089c2ab643bbbc4167791ccf9aee3ceac2c Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:59:15 -0800 Subject: [PATCH 6/6] exclude objectivec/docs in Package.swift --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index e240641..eefc171 100644 --- a/Package.swift +++ b/Package.swift @@ -36,7 +36,7 @@ let package = Package( .target(name: "OnnxRuntimeBindings", dependencies: ["onnxruntime"], path: "objectivec", - exclude: ["ReadMe.md", "format_objc.sh", "test", + exclude: ["ReadMe.md", "format_objc.sh", "test", "docs", "ort_checkpoint.mm", "ort_checkpoint_internal.h", "ort_training_session_internal.h",