// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #import "TensorHelper.h" #import #import @interface TensorHelperTest : XCTestCase @end @implementation TensorHelperTest template static void testCreateInputTensorT(const std::array &outValues, std::function &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(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 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{static_cast(outValues.size())}); XCTAssertEqual(inputTensor.GetTensorTypeAndShapeInfo().GetElementCount(), outValues.size()); const auto tensorData = inputTensor.GetTensorData(); for (size_t i = 0; i < outValues.size(); ++i) { XCTAssertEqual(tensorData[i], outValues[i]); } } - (void)testCreateInputTensorFloat { std::array outValues{std::numeric_limits::min(), 2.0f, std::numeric_limits::max()}; std::function convert = [](float_t value) { return [NSNumber numberWithFloat:value]; }; testCreateInputTensorT(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, JsTensorTypeFloat); } - (void)testCreateInputTensorDouble { std::array outValues{std::numeric_limits::min(), 2.0f, std::numeric_limits::max()}; std::function convert = [](double_t value) { return [NSNumber numberWithDouble:value]; }; testCreateInputTensorT(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE, JsTensorTypeDouble); } - (void)testCreateInputTensorBool { std::array outValues{false, true, true}; std::function convert = [](bool value) { return [NSNumber numberWithBool:value]; }; testCreateInputTensorT(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL, JsTensorTypeBool); } - (void)testCreateInputTensorInt8 { std::array outValues{std::numeric_limits::min(), 2, std::numeric_limits::max()}; std::function convert = [](int8_t value) { return [NSNumber numberWithChar:value]; }; testCreateInputTensorT(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8, JsTensorTypeByte); } - (void)testCreateInputTensorInt16 { std::array outValues{std::numeric_limits::min(), 2, std::numeric_limits::max()}; std::function convert = [](int16_t value) { return [NSNumber numberWithShort:value]; }; testCreateInputTensorT(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16, JsTensorTypeShort); } - (void)testCreateInputTensorInt32 { std::array outValues{std::numeric_limits::min(), 2, std::numeric_limits::max()}; std::function convert = [](int32_t value) { return [NSNumber numberWithInt:value]; }; testCreateInputTensorT(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32, JsTensorTypeInt); } - (void)testCreateInputTensorInt64 { std::array outValues{std::numeric_limits::min(), 2, std::numeric_limits::max()}; std::function convert = [](int64_t value) { return [NSNumber numberWithLongLong:value]; }; testCreateInputTensorT(outValues, convert, ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64, JsTensorTypeLong); } - (void)testCreateInputTensorString { std::array 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 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{static_cast(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 static void testCreateOutputTensorT(const std::array &outValues, std::function &convert, NSString *jsTensorType, NSString *testDataFileName, NSString *testDataFileExtension) { NSBundle *bundle = [NSBundle bundleForClass:[TensorHelperTest class]]; NSString *dataPath = [bundle pathForResource:testDataFileName ofType:testDataFileExtension]; std::unique_ptr ortEnv{new Ort::Env(ORT_LOGGING_LEVEL_INFO, "Default")}; Ort::SessionOptions sessionOptions; std::unique_ptr session{new Ort::Session(*ortEnv, [dataPath UTF8String], sessionOptions)}; Ort::AllocatorWithDefaultOptions ortAllocator; std::vector allocations; std::vector inputNames; inputNames.reserve(session->GetInputCount()); for (size_t i = 0; i < session->GetInputCount(); ++i) { auto inputName = session->GetInputName(i, ortAllocator); allocations.emplace_back(ortAllocator, inputName, strlen(inputName) + 1); inputNames.emplace_back(inputName); } std::vector outputNames; outputNames.reserve(session->GetOutputCount()); for (size_t i = 0; i < session->GetOutputCount(); ++i) { auto outputName = session->GetOutputName(i, ortAllocator); allocations.emplace_back(ortAllocator, outputName, strlen(outputName) + 1); outputNames.emplace_back(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(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::Value inputTensor = [TensorHelper createInputTensor:inputTensorMap ortAllocator:ortAllocator allocations:allocations]; std::vector 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 outValues{std::numeric_limits::min(), 1.0f, 2.0f, 3.0f, std::numeric_limits::max()}; std::function convert = [](float_t value) { return [NSNumber numberWithFloat:value]; }; testCreateOutputTensorT(outValues, convert, JsTensorTypeFloat, @"test_types_float", @"ort"); } - (void)testCreateOutputTensorDouble { std::array outValues{std::numeric_limits::min(), 1.0f, 2.0f, 3.0f, std::numeric_limits::max()}; std::function convert = [](double_t value) { return [NSNumber numberWithDouble:value]; }; testCreateOutputTensorT(outValues, convert, JsTensorTypeDouble, @"test_types_double", @"ort"); } - (void)testCreateOutputTensorBool { std::array outValues{false, true, true, false, true}; std::function convert = [](bool value) { return [NSNumber numberWithBool:value]; }; testCreateOutputTensorT(outValues, convert, JsTensorTypeBool, @"test_types_bool", @"ort"); } - (void)testCreateOutputTensorInt8 { std::array outValues{std::numeric_limits::min(), 1, -2, 3, std::numeric_limits::max()}; std::function convert = [](int8_t value) { return [NSNumber numberWithChar:value]; }; testCreateOutputTensorT(outValues, convert, JsTensorTypeByte, @"test_types_int8", @"ort"); } - (void)testCreateOutputTensorInt32 { std::array outValues{std::numeric_limits::min(), 1, -2, 3, std::numeric_limits::max()}; std::function convert = [](int32_t value) { return [NSNumber numberWithInt:value]; }; testCreateOutputTensorT(outValues, convert, JsTensorTypeInt, @"test_types_int32", @"ort"); } - (void)testCreateOutputTensorInt64 { std::array outValues{std::numeric_limits::min(), 1, -2, 3, std::numeric_limits::max()}; std::function convert = [](int64_t value) { return [NSNumber numberWithLongLong:value]; }; testCreateOutputTensorT(outValues, convert, JsTensorTypeLong, @"test_types_int64", @"ort"); } @end