From 1aba5f94c46efbdab4ca0636584bc50ba84534a8 Mon Sep 17 00:00:00 2001 From: Ettore Tiotto Date: Wed, 9 Feb 2022 18:16:18 -0500 Subject: [PATCH] Generalize CategoryMapper test. Signed-off-by: Ettore Tiotto --- include/onnx-mlir/Runtime/OMTensor.h | 16 +- src/Runtime/OMTensor.inc | 55 +++--- src/Runtime/OMTensorHelper.h | 11 +- test/backend-cpp/TestCategoryMapper.cpp | 213 ++++++++++++++++-------- 4 files changed, 192 insertions(+), 103 deletions(-) diff --git a/include/onnx-mlir/Runtime/OMTensor.h b/include/onnx-mlir/Runtime/OMTensor.h index e9f257c864..3191c24f2a 100644 --- a/include/onnx-mlir/Runtime/OMTensor.h +++ b/include/onnx-mlir/Runtime/OMTensor.h @@ -141,7 +141,7 @@ void omTensorDestroy(OMTensor *tensor); * @return pointer to the data buffer of the OMTensor, * NULL if the data buffer is not set. */ -void *omTensorGetDataPtr(OMTensor *tensor); +void *omTensorGetDataPtr(const OMTensor *tensor); /** * \brief OMTensor data shape getter. @@ -155,7 +155,7 @@ void *omTensorGetDataPtr(OMTensor *tensor); * @param tensor pointer to the OMTensor * @return pointer to the data shape array. */ -int64_t *omTensorGetShape(OMTensor *tensor); +int64_t *omTensorGetShape(const OMTensor *tensor); /** * \brief OMTensor data shape setter. @@ -185,7 +185,7 @@ void omTensorSetShape(OMTensor *tensor, int64_t *shape); * @param tensor pointer to the OMTensor * @return pointer to the data strides array. */ -int64_t *omTensorGetStrides(OMTensor *tensor); +int64_t *omTensorGetStrides(const OMTensor *tensor); /** * \brief OMTensor data strides setter @@ -230,7 +230,7 @@ void omTensorSetStridesWithPyArrayStrides( * @param tensor pointer to the OMTensor * @return ONNX data type of the data buffer elements. */ -OM_DATA_TYPE omTensorGetDataType(OMTensor *tensor); +OM_DATA_TYPE omTensorGetDataType(const OMTensor *tensor); /** * \brief OMTensor data type setter @@ -253,7 +253,7 @@ static inline int64_t getDataTypeSize(OM_DATA_TYPE dataType) { * @param tensor pointer to the OMTensor * @return the total size of the data buffer in bytes. */ -int64_t omTensorGetBufferSize(OMTensor *tensor); +int64_t omTensorGetBufferSize(const OMTensor *tensor); /** * \brief OMTensor rank getter @@ -261,7 +261,7 @@ int64_t omTensorGetBufferSize(OMTensor *tensor); * @param tensor, pointer to the OMTensor * @return rank of data shape and strides of the OMTensor. */ -int64_t omTensorGetRank(OMTensor *tensor); +int64_t omTensorGetRank(const OMTensor *tensor); /** * \brief OMTensor number of elements getter @@ -269,14 +269,14 @@ int64_t omTensorGetRank(OMTensor *tensor); * @param tensor, pointer to the OMTensor * @return the number of elements in the data buffer. */ -int64_t omTensorGetNumElems(OMTensor *tensor); +int64_t omTensorGetNumElems(const OMTensor *tensor); /** * \brief OMTensor owning flag getter * * @return owning flag of the OMTensor. */ -int64_t omTensorGetOwning(OMTensor *tensor); +int64_t omTensorGetOwning(const OMTensor *tensor); /** * \brief OMTensor owning flag setter diff --git a/src/Runtime/OMTensor.inc b/src/Runtime/OMTensor.inc index 792afda453..d6fa8ff7be 100644 --- a/src/Runtime/OMTensor.inc +++ b/src/Runtime/OMTensor.inc @@ -4,7 +4,7 @@ //===--------- OMTensor.inc - C/C++ Neutral OMTensor Implementation--------===// // -// Copyright 2019-2020 The IBM Research Authors. +// Copyright 2019-2022 The IBM Research Authors. // // ============================================================================= // @@ -225,7 +225,7 @@ void omTensorDestroy(OMTensor *tensor) { } /* OMTensor data getter */ -void *omTensorGetDataPtr(OMTensor *tensor) { return tensor->_alignedPtr; } +void *omTensorGetDataPtr(const OMTensor *tensor) { return tensor->_alignedPtr; } /** * OMTensor allocated and aligned pointer setter. @@ -255,7 +255,7 @@ void omTensorSetDataPtr( } /* OMTensor data shape getter */ -int64_t *omTensorGetShape(OMTensor *tensor) { return tensor->_shape; } +int64_t *omTensorGetShape(const OMTensor *tensor) { return tensor->_shape; } /* OMTensor data shape setter */ void omTensorSetShape(OMTensor *tensor, int64_t *shape) { @@ -264,7 +264,7 @@ void omTensorSetShape(OMTensor *tensor, int64_t *shape) { } /* OMTensor data strides getter */ -int64_t *omTensorGetStrides(OMTensor *tensor) { return tensor->_strides; } +int64_t *omTensorGetStrides(const OMTensor *tensor) { return tensor->_strides; } /* OMTensor data strides setter */ void omTensorSetStrides(OMTensor *tensor, int64_t *strides) { @@ -281,7 +281,9 @@ void omTensorSetStridesWithPyArrayStrides( } /* OMTensor data type getter */ -OM_DATA_TYPE omTensorGetDataType(OMTensor *tensor) { return tensor->_dataType; } +OM_DATA_TYPE omTensorGetDataType(const OMTensor *tensor) { + return tensor->_dataType; +} /* OMTensor data type setter */ void omTensorSetDataType(OMTensor *tensor, OM_DATA_TYPE dataType) { @@ -289,16 +291,16 @@ void omTensorSetDataType(OMTensor *tensor, OM_DATA_TYPE dataType) { } /* OMTensor data buffer size getter */ -int64_t omTensorGetBufferSize(OMTensor *tensor) { +int64_t omTensorGetBufferSize(const OMTensor *tensor) { return getNumElems(tensor->_shape, tensor->_rank) * getDataTypeSize(tensor->_dataType); } /* OMTensor rank getter */ -int64_t omTensorGetRank(OMTensor *tensor) { return tensor->_rank; } +int64_t omTensorGetRank(const OMTensor *tensor) { return tensor->_rank; } /* OMTensor number of elements getter */ -int64_t omTensorGetNumElems(OMTensor *tensor) { +int64_t omTensorGetNumElems(const OMTensor *tensor) { // Using signed indices helps detect when index falls below 0. // Verify that strides are dense, meaning that there're // no skipping elements. @@ -317,7 +319,7 @@ int64_t omTensorGetNumElems(OMTensor *tensor) { * * @return owning flag of the OMTensor. */ -int64_t omTensorGetOwning(OMTensor *tensor) { return tensor->_owning; } +int64_t omTensorGetOwning(const OMTensor *tensor) { return tensor->_owning; } /** * OMTensor owning flag setter. @@ -336,7 +338,7 @@ void omTensorSetOwning(OMTensor *tensor, int64_t owning) { * @return pointer to the allocated data buffer of the OMTensor, * NULL if the allocated data buffer is not set. */ -void *omTensorGetAllocatedPtr(OMTensor *tensor) { +void *omTensorGetAllocatedPtr(const OMTensor *tensor) { return tensor->_allocatedPtr; } @@ -403,25 +405,25 @@ OMTensor *omTensorCreateWithRandomData( /* Access an element (by reference) at offset computed by index array */ template -T &omTensorGetElem(OMTensor *omt, std::vector indexes) { +T &omTensorGetElem(const OMTensor *omt, std::vector indexes) { int64_t elemOffset = omTensorComputeElemOffset(omt, indexes); return ((T *)omt->_alignedPtr)[elemOffset]; } /* Access an element (by reference) at linear offset */ template -T &omTensorGetElemByOffset(OMTensor *omt, int64_t index) { +T &omTensorGetElemByOffset(const OMTensor *omt, int64_t index) { return ((T *)omt->_alignedPtr)[index]; } /* Compute strides vector from shape vector */ -std::vector omTensorComputeStridesFromShape(OMTensor *omt) { +std::vector omTensorComputeStridesFromShape(const OMTensor *omt) { return computeStridesFromShape(omt->_shape, omt->_rank); } /* Compute linear element offset from multi-dimensional index array */ int64_t omTensorComputeElemOffset( - OMTensor *omt, std::vector &indexes) { + const OMTensor *omt, std::vector &indexes) { return computeElemOffset(omt->_strides, omt->_rank, indexes); } @@ -529,20 +531,25 @@ template OMTensor *omTensorCreateWithRandomData( template OMTensor *omTensorCreateWithRandomData( std::vector shape, double lbound, double ubound); -template bool &omTensorGetElem(OMTensor *, std::vector indexes); +template bool &omTensorGetElem( + const OMTensor *, std::vector indexes); template int32_t &omTensorGetElem( - OMTensor *, std::vector indexes); + const OMTensor *, std::vector indexes); template int64_t &omTensorGetElem( - OMTensor *, std::vector indexes); + const OMTensor *, std::vector indexes); template float &omTensorGetElem( - OMTensor *, std::vector indexes); + const OMTensor *, std::vector indexes); template double &omTensorGetElem( - OMTensor *, std::vector indexes); - -template int32_t &omTensorGetElemByOffset(OMTensor *, int64_t index); -template int64_t &omTensorGetElemByOffset(OMTensor *, int64_t index); -template float &omTensorGetElemByOffset(OMTensor *, int64_t indexs); -template double &omTensorGetElemByOffset(OMTensor *, int64_t index); + const OMTensor *, std::vector indexes); + +template int32_t &omTensorGetElemByOffset( + const OMTensor *, int64_t index); +template int64_t &omTensorGetElemByOffset( + const OMTensor *, int64_t index); +template float &omTensorGetElemByOffset( + const OMTensor *, int64_t indexs); +template double &omTensorGetElemByOffset( + const OMTensor *, int64_t index); template bool omTensorAreTwoOmtsClose( OMTensor *a, OMTensor *b, float rtol, float atol); diff --git a/src/Runtime/OMTensorHelper.h b/src/Runtime/OMTensorHelper.h index 739d399198..64b1640099 100644 --- a/src/Runtime/OMTensorHelper.h +++ b/src/Runtime/OMTensorHelper.h @@ -108,7 +108,7 @@ OMTensor *omTensorCreateWithRandomData( * @return typed element by reference at the offset computed by the index array. */ template -T &omTensorGetElem(OMTensor *omt, std::vector indexes); +T &omTensorGetElem(const OMTensor *omt, std::vector indexes); /** * OMTensor data element getter by index @@ -118,7 +118,7 @@ T &omTensorGetElem(OMTensor *omt, std::vector indexes); * @return typed element by reference at the linear offset. */ template -T &omTensorGetElemByOffset(OMTensor *omt, int64_t index); +T &omTensorGetElemByOffset(const OMTensor *omt, int64_t index); /** * OMTensor strides computation @@ -126,7 +126,7 @@ T &omTensorGetElemByOffset(OMTensor *omt, int64_t index); * @param omt, pointer to the OMTensor * @return data strides of the OMTensor computed from the data sizes. */ -std::vector omTensorComputeStridesFromShape(OMTensor *omt); +std::vector omTensorComputeStridesFromShape(const OMTensor *omt); /** * OMTensor linear offset computation @@ -135,7 +135,8 @@ std::vector omTensorComputeStridesFromShape(OMTensor *omt); * @param indexes, multi-dimensional index array * @return linear offset. */ -int64_t omTensorComputeElemOffset(OMTensor *omt, std::vector &indexes); +int64_t omTensorComputeElemOffset( + const OMTensor *omt, std::vector &indexes); /** * OMTensor index set computation @@ -145,7 +146,7 @@ int64_t omTensorComputeElemOffset(OMTensor *omt, std::vector &indexes); * that can be used to access this OMTensor's constituent elements) * for the whole OMTensor. */ -std::vector> omTensorComputeIndexSet(OMTensor *omt); +std::vector> omTensorComputeIndexSet(const OMTensor *omt); /** * OMTensor "distance" computation diff --git a/test/backend-cpp/TestCategoryMapper.cpp b/test/backend-cpp/TestCategoryMapper.cpp index 70bbd6bcf5..b192b37d8c 100644 --- a/test/backend-cpp/TestCategoryMapper.cpp +++ b/test/backend-cpp/TestCategoryMapper.cpp @@ -39,22 +39,17 @@ class CategoryMapperTester { // Test CategoryMapper (with an input tensor of int64_t numbers). bool testInt64ToStr(const CMAttributes &attributes, ArrayRef input, - ArrayRef expectedOutput) { - assert(input.size() == expectedOutput.size() && - "Expecting input/output to have the same size"); - - int64_t inputShape[] = {static_cast(input.size())}; - auto inputType = RankedTensorType::get( - inputShape, modelBuilder.getBuilder().getI64Type()); + ArrayRef expOutput) { + assert(input.size() == expOutput.size() && + "Expecting input/expOutput to have the same size"); + + // Create the test function. + int64_t shape[1] = {static_cast(input.size())}; + auto inputType = + RankedTensorType::get(shape, modelBuilder.getBuilder().getI64Type()); auto outputType = RankedTensorType::get( - inputShape, ONNXStringType::get(&modelBuilder.getContext())); - - // Create the test code. - llvm::SmallVector inputsType{inputType}, outputsType{outputType}; - FuncOp funcOp = - modelBuilder.createEmptyTestFunction(inputsType, outputsType); - createCategoryMapper(outputType, attributes, funcOp); - modelBuilder.createEntryPoint(funcOp); + shape, ONNXStringType::get(&modelBuilder.getContext())); + createTestFunction(inputType, outputType, attributes); // Compile the test. if (!modelBuilder.compileTest( @@ -64,46 +59,85 @@ class CategoryMapperTester { } // Run the test and verify the result. - std::vector inputOMTs, expectedOutputOMTs; auto inputOMT = onnx_mlir::OMTensorUniquePtr( - omTensorCreate(static_cast(const_cast(input.data())), - inputShape, 1 /*rank*/, ONNX_TYPE_INT64), + createOMTensor(input, shape, 1, ONNX_TYPE_INT64), omTensorDestroy); - auto expectedOutputOMT = onnx_mlir::OMTensorUniquePtr( - omTensorCreate(static_cast( - const_cast(expectedOutput.data())), - inputShape, 1 /*rank*/, ONNX_TYPE_STRING), + auto expOutputOMT = onnx_mlir::OMTensorUniquePtr( + createOMTensor(expOutput, shape, 1, ONNX_TYPE_STRING), omTensorDestroy); + LLVM_DEBUG({ + llvm::dbgs() << "input: "; + printTensorData(inputOMT.get()); + llvm::dbgs() << "expected output: "; + printTensorData(expOutputOMT.get()); + }); + + std::vector inputOMTs, expOutputOMTs; + inputOMTs.emplace_back(move(inputOMT)); + expOutputOMTs.emplace_back(move(expOutputOMT)); + + return modelBuilder.runAndVerifyTest( + inputOMTs, expOutputOMTs, verifyResults); + } + + // Test CategoryMapper (with an input tensor of strings). + bool testStrToInt64(const CMAttributes &attributes, + ArrayRef input, ArrayRef expOutput) { + assert(input.size() == expOutput.size() && + "Expecting input/expOutput to have the same size"); + // Create the test function. + int64_t shape[1] = {static_cast(input.size())}; + auto inputType = RankedTensorType::get( + shape, ONNXStringType::get(&modelBuilder.getContext())); + auto outputType = + RankedTensorType::get(shape, modelBuilder.getBuilder().getI64Type()); + createTestFunction(inputType, outputType, attributes); + + // Compile the test. + if (!modelBuilder.compileTest( + {{onnx_mlir::OptionKind::CompilerOptLevel, "3"}})) { + llvm::errs() << "Failed to compile the test case\n"; + return false; + } + + // Run the test and verify the result. + auto inputOMT = onnx_mlir::OMTensorUniquePtr( + createOMTensor(input, shape, 1, ONNX_TYPE_STRING), + omTensorDestroy); + auto expOutputOMT = onnx_mlir::OMTensorUniquePtr( + createOMTensor(expOutput, shape, 1, ONNX_TYPE_INT64), + omTensorDestroy); LLVM_DEBUG({ llvm::dbgs() << "input: "; - int64_t *inputDataPtr = - static_cast(omTensorGetDataPtr(inputOMT.get())); - for (int i = 0; i < omTensorGetNumElems(inputOMT.get()); ++i) - llvm::dbgs() << inputDataPtr[i] << " "; - llvm::dbgs() << "\n"; - - llvm::errs() << "expectedOutput: "; - const char **outputDataPtr = static_cast( - omTensorGetDataPtr(expectedOutputOMT.get())); - for (int i = 0; i < omTensorGetNumElems(expectedOutputOMT.get()); ++i) - llvm::dbgs() << outputDataPtr[i] << " "; - llvm::dbgs() << "\n"; + printTensorData(inputOMT.get()); + llvm::dbgs() << "expected output: "; + printTensorData(expOutputOMT.get()); }); + std::vector inputOMTs, expOutputOMTs; inputOMTs.emplace_back(move(inputOMT)); - expectedOutputOMTs.emplace_back(move(expectedOutputOMT)); + expOutputOMTs.emplace_back(move(expOutputOMT)); return modelBuilder.runAndVerifyTest( - inputOMTs, expectedOutputOMTs, verifyFunction); + inputOMTs, expOutputOMTs, verifyResults); } // Prepare for a new test. void reset() { modelBuilder.reset(); } private: - // Create the category mapper test code into the given function, and add the - // function into the given module. + // Create the function to test. + void createTestFunction( + Type inputType, Type outputType, const CMAttributes &attributes) { + llvm::SmallVector inputsType{inputType}, outputsType{outputType}; + FuncOp funcOp = + modelBuilder.createEmptyTestFunction(inputsType, outputsType); + createCategoryMapper(outputType, attributes, funcOp); + modelBuilder.createEntryPoint(funcOp); + } + + // Create the category mapper operator, and insert it into the test function. void createCategoryMapper( Type outputType, const CMAttributes &attributes, FuncOp &funcOp) { ModuleOp &module = modelBuilder.getModule(); @@ -123,45 +157,98 @@ class CategoryMapperTester { module.push_back(funcOp); } - static bool verifyFunction(OMTensor *out, OMTensor *expected) { - // Verify that the output tensor has the expected rank/extents. - if (omTensorGetRank(out) != omTensorGetRank(expected)) { - llvm::errs() << "Output tensor has rank " << omTensorGetRank(out) - << ", expecting " << omTensorGetRank(expected) << "\n"; + // Verify that the output tensor has the expected rank. + static bool verifyRank(const OMTensor &out, int64_t rank) { + if (omTensorGetRank(&out) != rank) { + llvm::errs() << "Output tensor has rank " << omTensorGetRank(&out) + << ", expecting " << rank << "\n"; return false; } - if (omTensorGetNumElems(out) != omTensorGetNumElems(expected)) { - llvm::errs() << "Output tensor has " << omTensorGetNumElems(out) - << "elements, expecting " << omTensorGetNumElems(expected) - << "\n"; + return true; + } + + // Verify that the output tensor has the expected number of elements. + static bool verifyNumElements(const OMTensor &out, int64_t numElems) { + if (omTensorGetNumElems(&out) != numElems) { + llvm::errs() << "Output tensor has " << omTensorGetNumElems(&out) + << " elements, expecting " << numElems << "\n"; return false; } + return true; + } + + template + static bool compareEqual(T val, T expectedVal) { + return val == expectedVal; + } + + // Verification function. + // This function will be called back by the ModelBuilder. + template + static bool verifyResults(const OMTensor *out, const OMTensor *expected) { + if (!verifyRank(*out, omTensorGetRank(expected))) + return false; + if (!verifyNumElements(*out, omTensorGetNumElems(expected))) + return false; // Verify that the output tensor contains the expected result. - auto outDataPtr = (const char **)omTensorGetDataPtr(out); - auto expectedDataPtr = (const char **)(omTensorGetDataPtr(expected)); + const auto *outDataPtr = static_cast(omTensorGetDataPtr(out)); + const auto *expDataPtr = static_cast(omTensorGetDataPtr(expected)); LLVM_DEBUG(llvm::dbgs() << "Result Verification:\n"); - for (int i = 0; i < omTensorGetNumElems(out); i++) { - const char *str = outDataPtr[i]; - const char *expectedStr = expectedDataPtr[i]; - LLVM_DEBUG(llvm::dbgs() - << "str: " << str << ", expectedStr: " << expectedStr << "\n"); - - if (strcmp(str, expectedStr) != 0) { - llvm::errs() << "Output tensor contains \"" << str - << "\" at index = " << i << ", expecting \"" << expectedStr - << "\"\n"; + for (int64_t i = 0; i < omTensorGetNumElems(out); i++) { + LLVM_DEBUG(llvm::dbgs().indent(2) + << "Got: " << outDataPtr[i] << ", expected: " << expDataPtr[i] + << "\n"); + + if (!compareEqual(outDataPtr[i], expDataPtr[i])) { + llvm::errs() << "Output tensor contains \"" << outDataPtr[i] + << "\" at index = " << i << ", expecting \"" + << expDataPtr[i] << "\"\n"; return false; } } + LLVM_DEBUG(llvm::dbgs() << "Result is OK.\n"); return true; } + + // Utility function used to create an OMTensor. + template + static OMTensor *createOMTensor( + ArrayRef array, int64_t shape[], int64_t rank, OM_DATA_TYPE dtype) { + return omTensorCreate( + static_cast(const_cast(array.data())), shape, rank, dtype); + } + + // Print the data pointed to by the given OMtensor. + template + static void printTensorData(const OMTensor *omt) { + T *dataPtr = static_cast(omTensorGetDataPtr(omt)); + for (int64_t i = 0; i < omTensorGetNumElems(omt); ++i) + llvm::dbgs() << dataPtr[i] << " "; + llvm::dbgs() << "\n"; + } }; +template <> +bool CategoryMapperTester::compareEqual( + const char *str, const char *expectedStr) { + return strcmp(str, expectedStr) == 0; +} + } // namespace +bool testInt64ToStr() { + MLIRContext ctx; + CategoryMapperTester categoryMapperTester(ctx); + const CategoryMapperTester::CMAttributes attributes = {{1, 2, 3, 4, 5}, + {"cat", "dog", "human", "tiger", "beaver"}, -1, "unknown"}; + + return categoryMapperTester.testInt64ToStr( + attributes, {1, 2, 3, 4, 5}, {"cat", "dog", "human", "tiger", "beaver"}); +} + int main(int argc, char *argv[]) { llvm::FileRemover remover( BackendCppTests::ModelBuilder::getSharedLibName(SharedLibBaseName)); @@ -169,13 +256,7 @@ int main(int argc, char *argv[]) { llvm::cl::ParseCommandLineOptions( argc, argv, "TestCategoryMapper\n", nullptr, "TEST_ARGS"); - MLIRContext ctx; - CategoryMapperTester categoryMapperTester(ctx); - const CategoryMapperTester::CMAttributes attributes = {{1, 2, 3, 4, 5}, - {"cat", "dog", "human", "tiger", "beaver"}, -1, "unknown"}; - - if (!categoryMapperTester.testInt64ToStr(attributes, {1, 2, 3, 4, 5}, - {"cat", "dog", "human", "tiger", "beaver"})) + if (!testInt64ToStr()) return 1; return 0;