onnxruntime/js/react_native/ios/OnnxruntimeModuleTest/TensorHelperTest.mm
Scott McKay 078ceab1db
Use full ORT package for onnxruntime-react-native. (#13037)
**Description**: 
Use full ORT package for onnxruntime-react-native.

Left the params required for the mobile build in comments so they're
easily discovered if we need to create onnxruntime-react-native-mobile
in the future.

**Motivation and Context**
Remove barrier to using ORT with react native as the mobile package that
was being used supports a limited range of opsets/operators/types, and
requires ORT format models. The full package will run any model.
2022-09-23 07:20:03 +10:00

246 lines
11 KiB
Text

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "TensorHelper.h"
#import <XCTest/XCTest.h>
#import <onnxruntime/onnxruntime_cxx_api.h>
#include <vector>
@interface TensorHelperTest : XCTestCase
@end
@implementation TensorHelperTest
template <typename T>
static void testCreateInputTensorT(const std::array<T, 3> &outValues, std::function<NSNumber *(T value)> &convert,
ONNXTensorElementDataType onnxType, NSString *jsTensorType) {
NSMutableDictionary *inputTensorMap = [NSMutableDictionary dictionary];
// dims
NSArray *dims = @[ [NSNumber numberWithLong:outValues.size()] ];
inputTensorMap[@"dims"] = dims;
// type
inputTensorMap[@"type"] = jsTensorType;
// encoded data
size_t byteBufferSize = sizeof(T) * outValues.size();
unsigned char *byteBuffer = static_cast<unsigned char *>(malloc(byteBufferSize));
NSData *byteBufferRef = [NSData dataWithBytesNoCopy:byteBuffer length:byteBufferSize];
T *typePtr = (T *)[byteBufferRef bytes];
for (size_t i = 0; i < outValues.size(); ++i) {
typePtr[i] = outValues[i];
}
NSString *dataEncoded = [byteBufferRef base64EncodedStringWithOptions:0];
inputTensorMap[@"data"] = dataEncoded;
Ort::AllocatorWithDefaultOptions ortAllocator;
std::vector<Ort::MemoryAllocation> allocations;
Ort::Value inputTensor = [TensorHelper createInputTensor:inputTensorMap
ortAllocator:ortAllocator
allocations:allocations];
XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetElementType(), onnxType);
XCTAssertTrue(inputTensor.IsTensor());
XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetDimensionsCount(), 1);
XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetShape(),
std::vector<int64_t>{static_cast<int64_t>(outValues.size())});
XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetElementCount(), outValues.size());
const auto tensorData = inputTensor.GetTensorData<T>();
for (size_t i = 0; i < outValues.size(); ++i) {
XCTAssertEqual(tensorData[i], outValues[i]);
}
}
- (void)testCreateInputTensorFloat {
std::array<float_t, 3> outValues{std::numeric_limits<float_t>::min(), 2.0f, std::numeric_limits<float_t>::max()};
std::function<NSNumber *(float_t value)> convert = [](float_t value) { return [NSNumber numberWithFloat:value]; };
testCreateInputTensorT<float_t>(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, JsTensorTypeFloat);
}
- (void)testCreateInputTensorDouble {
std::array<double_t, 3> outValues{std::numeric_limits<double_t>::min(), 2.0f, std::numeric_limits<double_t>::max()};
std::function<NSNumber *(double_t value)> convert = [](double_t value) { return [NSNumber numberWithDouble:value]; };
testCreateInputTensorT<double_t>(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE, JsTensorTypeDouble);
}
- (void)testCreateInputTensorBool {
std::array<bool, 3> outValues{false, true, true};
std::function<NSNumber *(bool value)> convert = [](bool value) { return [NSNumber numberWithBool:value]; };
testCreateInputTensorT<bool>(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL, JsTensorTypeBool);
}
- (void)testCreateInputTensorInt8 {
std::array<int8_t, 3> outValues{std::numeric_limits<int8_t>::min(), 2, std::numeric_limits<int8_t>::max()};
std::function<NSNumber *(int8_t value)> convert = [](int8_t value) { return [NSNumber numberWithChar:value]; };
testCreateInputTensorT<int8_t>(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8, JsTensorTypeByte);
}
- (void)testCreateInputTensorInt16 {
std::array<int16_t, 3> outValues{std::numeric_limits<int16_t>::min(), 2, std::numeric_limits<int16_t>::max()};
std::function<NSNumber *(int16_t value)> convert = [](int16_t value) { return [NSNumber numberWithShort:value]; };
testCreateInputTensorT<int16_t>(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16, JsTensorTypeShort);
}
- (void)testCreateInputTensorInt32 {
std::array<int32_t, 3> outValues{std::numeric_limits<int32_t>::min(), 2, std::numeric_limits<int32_t>::max()};
std::function<NSNumber *(int32_t value)> convert = [](int32_t value) { return [NSNumber numberWithInt:value]; };
testCreateInputTensorT<int32_t>(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32, JsTensorTypeInt);
}
- (void)testCreateInputTensorInt64 {
std::array<int64_t, 3> outValues{std::numeric_limits<int64_t>::min(), 2, std::numeric_limits<int64_t>::max()};
std::function<NSNumber *(int64_t value)> convert = [](int64_t value) { return [NSNumber numberWithLongLong:value]; };
testCreateInputTensorT<int64_t>(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64, JsTensorTypeLong);
}
- (void)testCreateInputTensorString {
std::array<std::string, 3> outValues{"a", "b", "c"};
NSMutableDictionary *inputTensorMap = [NSMutableDictionary dictionary];
// dims
NSArray *dims = @[ [NSNumber numberWithLong:outValues.size()] ];
inputTensorMap[@"dims"] = dims;
// type
inputTensorMap[@"type"] = JsTensorTypeString;
// data
NSMutableArray *data = [NSMutableArray array];
for (auto value : outValues) {
[data addObject:[NSString stringWithUTF8String:value.c_str()]];
}
inputTensorMap[@"data"] = data;
Ort::AllocatorWithDefaultOptions ortAllocator;
std::vector<Ort::MemoryAllocation> allocations;
Ort::Value inputTensor = [TensorHelper createInputTensor:inputTensorMap
ortAllocator:ortAllocator
allocations:allocations];
XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetElementType(), ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING);
XCTAssertTrue(inputTensor.IsTensor());
XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetDimensionsCount(), 1);
XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetShape(),
std::vector<int64_t>{static_cast<int64_t>(outValues.size())});
XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetElementCount(), outValues.size());
for (int i = 0; i < inputTensor.GetTensorTypeAndShapeInfo().GetElementCount(); ++i) {
size_t elementLength = inputTensor.GetStringTensorElementLength(i);
std::string element(elementLength, '\0');
inputTensor.GetStringTensorElement(elementLength, i, (void *)element.data());
XCTAssertEqual(element, outValues[i]);
}
}
template <typename T>
static void testCreateOutputTensorT(const std::array<T, 5> &outValues, std::function<NSNumber *(T value)> &convert,
NSString *jsTensorType, NSString *testDataFileName,
NSString *testDataFileExtension) {
NSBundle *bundle = [NSBundle bundleForClass:[TensorHelperTest class]];
NSString *dataPath = [bundle pathForResource:testDataFileName ofType:testDataFileExtension];
Ort::Env ortEnv{ORT_LOGGING_LEVEL_INFO, "Default"};
Ort::SessionOptions sessionOptions;
Ort::Session session{ortEnv, [dataPath UTF8String], sessionOptions};
Ort::AllocatorWithDefaultOptions ortAllocator;
std::vector<Ort::AllocatedStringPtr> names;
names.reserve(session.GetInputCount() + session.GetOutputCount());
std::vector<const char *> inputNames;
inputNames.reserve(session.GetInputCount());
for (size_t i = 0; i < session.GetInputCount(); ++i) {
auto inputName = session.GetInputNameAllocated(i, ortAllocator);
inputNames.emplace_back(inputName.get());
names.emplace_back(std::move(inputName));
}
std::vector<const char *> outputNames;
outputNames.reserve(session.GetOutputCount());
for (size_t i = 0; i < session.GetOutputCount(); ++i) {
auto outputName = session.GetOutputNameAllocated(i, ortAllocator);
outputNames.emplace_back(outputName.get());
names.emplace_back(std::move(outputName));
}
NSMutableDictionary *inputTensorMap = [NSMutableDictionary dictionary];
// dims
NSArray *dims = @[ [NSNumber numberWithLong:1], [NSNumber numberWithLong:outValues.size()] ];
inputTensorMap[@"dims"] = dims;
// type
inputTensorMap[@"type"] = jsTensorType;
// encoded data
size_t byteBufferSize = sizeof(T) * outValues.size();
unsigned char *byteBuffer = static_cast<unsigned char *>(malloc(byteBufferSize));
NSData *byteBufferRef = [NSData dataWithBytesNoCopy:byteBuffer length:byteBufferSize];
T *typePtr = (T *)[byteBufferRef bytes];
for (size_t i = 0; i < outValues.size(); ++i) {
typePtr[i] = outValues[i];
}
NSString *dataEncoded = [byteBufferRef base64EncodedStringWithOptions:0];
inputTensorMap[@"data"] = dataEncoded;
std::vector<Ort::MemoryAllocation> allocations;
Ort::Value inputTensor = [TensorHelper createInputTensor:inputTensorMap
ortAllocator:ortAllocator
allocations:allocations];
std::vector<Ort::Value> feeds;
feeds.emplace_back(std::move(inputTensor));
Ort::RunOptions runOptions;
auto output = session.Run(runOptions, inputNames.data(), feeds.data(), inputNames.size(), outputNames.data(),
outputNames.size());
NSDictionary *resultMap = [TensorHelper createOutputTensor:outputNames values:output];
XCTAssertTrue([[resultMap objectForKey:@"output"] isEqualToDictionary:inputTensorMap]);
}
- (void)testCreateOutputTensorFloat {
std::array<float_t, 5> outValues{std::numeric_limits<float_t>::min(), 1.0f, 2.0f, 3.0f,
std::numeric_limits<float_t>::max()};
std::function<NSNumber *(float_t value)> convert = [](float_t value) { return [NSNumber numberWithFloat:value]; };
testCreateOutputTensorT<float_t>(outValues, convert, JsTensorTypeFloat, @"test_types_float", @"ort");
}
- (void)testCreateOutputTensorDouble {
std::array<double_t, 5> outValues{std::numeric_limits<double_t>::min(), 1.0f, 2.0f, 3.0f,
std::numeric_limits<double_t>::max()};
std::function<NSNumber *(double_t value)> convert = [](double_t value) { return [NSNumber numberWithDouble:value]; };
testCreateOutputTensorT<double_t>(outValues, convert, JsTensorTypeDouble, @"test_types_double", @"onnx");
}
- (void)testCreateOutputTensorBool {
std::array<bool, 5> outValues{false, true, true, false, true};
std::function<NSNumber *(bool value)> convert = [](bool value) { return [NSNumber numberWithBool:value]; };
testCreateOutputTensorT<bool>(outValues, convert, JsTensorTypeBool, @"test_types_bool", @"onnx");
}
- (void)testCreateOutputTensorInt8 {
std::array<int8_t, 5> outValues{std::numeric_limits<int8_t>::min(), 1, -2, 3, std::numeric_limits<int8_t>::max()};
std::function<NSNumber *(int8_t value)> convert = [](int8_t value) { return [NSNumber numberWithChar:value]; };
testCreateOutputTensorT<int8_t>(outValues, convert, JsTensorTypeByte, @"test_types_int8", @"ort");
}
- (void)testCreateOutputTensorInt32 {
std::array<int32_t, 5> outValues{std::numeric_limits<int32_t>::min(), 1, -2, 3, std::numeric_limits<int32_t>::max()};
std::function<NSNumber *(int32_t value)> convert = [](int32_t value) { return [NSNumber numberWithInt:value]; };
testCreateOutputTensorT<int32_t>(outValues, convert, JsTensorTypeInt, @"test_types_int32", @"ort");
}
- (void)testCreateOutputTensorInt64 {
std::array<int64_t, 5> outValues{std::numeric_limits<int64_t>::min(), 1, -2, 3, std::numeric_limits<int64_t>::max()};
std::function<NSNumber *(int64_t value)> convert = [](int64_t value) { return [NSNumber numberWithLongLong:value]; };
testCreateOutputTensorT<int64_t>(outValues, convert, JsTensorTypeLong, @"test_types_int64", @"ort");
}
@end