diff options
author | Grant Watson <grant.watson@arm.com> | 2022-11-16 15:32:39 +0000 |
---|---|---|
committer | Eric Kunze <eric.kunze@arm.com> | 2022-12-15 16:41:27 +0000 |
commit | 64285a1f25e2c7b85ed1f00b7947403e92baea00 (patch) | |
tree | 6d29c54f6497741449339e808508c854ba6a2267 /reference_model/test | |
parent | b45db9a696f5df7b233f374248f329c16ee7ae64 (diff) | |
download | reference_model-64285a1f25e2c7b85ed1f00b7947403e92baea00.tar.gz |
Extend reference model API with eager operator execution entrypoints
- Adds a script to generate operators.h and operators.cc
- Adds jinja2 templates for generating operators.h and operators.cc
- Adds unit tests for a subset of the operators generated
- Includes the TOSA specification as a submodule
- Adds supporting C++ and header files
Signed-off-by: Grant Watson <grant.watson@arm.com>
Change-Id: I5b60db4c56113110d8e75fe1152525d258233f9c
Diffstat (limited to 'reference_model/test')
-rw-r--r-- | reference_model/test/model_runner_tests.cpp | 397 |
1 files changed, 309 insertions, 88 deletions
diff --git a/reference_model/test/model_runner_tests.cpp b/reference_model/test/model_runner_tests.cpp index 8304bc7..bb57657 100644 --- a/reference_model/test/model_runner_tests.cpp +++ b/reference_model/test/model_runner_tests.cpp @@ -17,8 +17,11 @@ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #endif -#include "model_runner.h" #include "general_utils.h" +#include "model_runner.h" +#include "operators.h" + +#include <numeric> // Remove conflicting REQUIRE definition between doctest and reference_model #undef REQUIRE @@ -33,125 +36,343 @@ void compareOutput(std::vector<T>& tensor1, std::vector<T>& tensor2, size_t size { for (size_t i = 0; i < size; ++i) { - CHECK((tensor1[i] == tensor2[i])); + CHECK_MESSAGE(tensor1[i] == doctest::Approx(tensor2[i]), ""); } } TEST_SUITE("model_runner") { -TEST_CASE("simple_add_f32_test") -{ - std::string test_root(std::string(PROJECT_ROOT) + "../examples/test_add_1x4x4x4_f32/"); - std::string tosa_model_file(test_root + "flatbuffer-tflite/test_add_1x4x4x4_f32.tosa"); - std::string input0_file(test_root + "placeholder_0.npy"); - std::string input1_file(test_root + "placeholder_1.npy"); - std::string expected_output_file(test_root + "tflite_result.npy"); + TEST_CASE("op_entry_add") + { + // Inputs/Outputs + tosa_datatype_t dt = tosa_datatype_fp32_t; + std::vector<int32_t> input_shape = { 2, 4, 4, 1 }; + std::vector<int32_t> output_shape = { 2, 4, 4, 1 }; + std::vector<float> srcData1(32, 4.0f); + std::vector<float> srcData2(32, 3.0f); + std::vector<float> dstData(32, 0.0f); + + tosa_tensor_t input1; + input1.shape = input_shape.data(); + input1.num_dims = input_shape.size(); + input1.data_type = dt; + input1.data = reinterpret_cast<uint8_t*>(srcData1.data()); + input1.size = srcData1.size() * sizeof(float); + + tosa_tensor_t input2; + input2.shape = input_shape.data(); + input2.num_dims = input_shape.size(); + input2.data_type = dt; + input2.data = reinterpret_cast<uint8_t*>(srcData2.data()); + input2.size = srcData2.size() * sizeof(float); + + tosa_tensor_t output; + output.shape = output_shape.data(); + output.num_dims = output_shape.size(); + output.data_type = dt; + output.data = reinterpret_cast<uint8_t*>(dstData.data()); + output.size = dstData.size() * sizeof(float); + + // Execution + auto status = tosa_run_add(input1, input2, output); + CHECK((status == tosa_status_valid)); + + // Compare results + std::vector<float> expectedData(8, 7.0f); + compareOutput(dstData, expectedData, expectedData.size()); + } - std::vector<std::string> input_names = { "TosaInput_0", "TosaInput_1" }; - std::string output_name = "TosaOutput_0"; + TEST_CASE("op_entry_avg_pool2d") + { + // Pool parameters + const int32_t kernel[2] = { 2, 2 }; + const int32_t stride[2] = { 2, 2 }; + const int32_t pad[4] = { 0, 0, 0, 0 }; + + // Inputs/Outputs + tosa_datatype_t dt = tosa_datatype_fp32_t; + std::vector<int32_t> input_shape = { 2, 4, 4, 1 }; + std::vector<int32_t> output_shape = { 2, 2, 2, 1 }; + std::vector<float> srcData(32, 7.0f); + std::vector<float> dstData(8, 0.f); + + tosa_tensor_t input; + input.shape = input_shape.data(); + input.num_dims = input_shape.size(); + input.data_type = dt; + input.data = reinterpret_cast<uint8_t*>(srcData.data()); + input.size = srcData.size() * sizeof(float); + + tosa_tensor_t output; + output.shape = output_shape.data(); + output.num_dims = output_shape.size(); + output.data_type = dt; + output.data = reinterpret_cast<uint8_t*>(dstData.data()); + output.size = dstData.size() * sizeof(float); + + // Execution + auto status = tosa_run_avg_pool2d(input, kernel, stride, pad, 0, 0, output); + CHECK((status == tosa_status_valid)); + + // Compare results + std::vector<float> expectedData(8, 7.0f); + compareOutput(dstData, expectedData, expectedData.size()); + } - std::vector<int32_t> input0_shape = { 1, 4, 4, 1 }; - std::vector<int32_t> input1_shape = { 1, 4, 4, 4 }; - std::vector<int32_t> output_shape = { 1, 4, 4, 4 }; + TEST_CASE("op_entry_conv2d") + { + // Conv parameters + const int32_t stride[2] = { 1, 1 }; + const int32_t pad[4] = { 0, 0, 0, 0 }; + const int32_t dilation[2] = { 1, 1 }; + + // Inputs/Outputs + tosa_datatype_t dt = tosa_datatype_fp32_t; + std::vector<int32_t> input_shape = { 1, 32, 32, 8 }; + std::vector<int32_t> output_shape = { 1, 32, 32, 16 }; + std::vector<int32_t> weight_shape = { 16, 1, 1, 8 }; + std::vector<int32_t> bias_shape = { 16 }; + std::vector<float> srcData(32 * 32 * 8, 1.0f); + std::vector<float> dstData(32 * 32 * 16, 0.f); + std::vector<float> biasData(16, 0.f); + std::vector<float> weightData(16 * 8, 1.0f); + + tosa_tensor_t input; + input.shape = input_shape.data(); + input.num_dims = input_shape.size(); + input.data_type = dt; + input.data = reinterpret_cast<uint8_t*>(srcData.data()); + input.size = srcData.size() * sizeof(float); + + tosa_tensor_t weight; + weight.shape = weight_shape.data(); + weight.num_dims = weight_shape.size(); + weight.data_type = dt; + weight.data = reinterpret_cast<uint8_t*>(weightData.data()); + weight.size = weightData.size() * sizeof(float); + + tosa_tensor_t bias; + bias.shape = bias_shape.data(); + bias.num_dims = bias_shape.size(); + bias.data_type = dt; + bias.data = reinterpret_cast<uint8_t*>(biasData.data()); + bias.size = biasData.size() * sizeof(float); + + tosa_tensor_t output; + output.shape = output_shape.data(); + output.num_dims = output_shape.size(); + output.data_type = dt; + output.data = reinterpret_cast<uint8_t*>(dstData.data()); + output.size = dstData.size() * sizeof(float); + + const int32_t input_zp = 0; + const int32_t weight_zp = 0; + + // Execution + auto status = tosa_run_conv2d(input, weight, bias, pad, stride, dilation, input_zp, weight_zp, output); + CHECK((status == tosa_status_valid)); + + // Compare results + std::vector<float> expectedData(32 * 32 * 16, 8.0f); + compareOutput(dstData, expectedData, expectedData.size()); + } - std::vector<std::vector<float>> inputs(input_names.size()); - std::vector<float> actual_outputs = { }; - std::vector<float> expected_outputs = { }; + TEST_CASE("op_entry_max_pool2d") + { + // Pool parameters + const int32_t kernel[2] = { 2, 2 }; + const int32_t stride[2] = { 2, 2 }; + const int32_t pad[4] = { 0, 0, 0, 0 }; + + // Inputs/Outputs + tosa_datatype_t dt = tosa_datatype_fp32_t; + std::vector<int32_t> input_shape = { 2, 4, 4, 1 }; + std::vector<int32_t> output_shape = { 2, 2, 2, 1 }; + std::vector<float> srcData(32); + std::vector<float> dstData(8, 0.f); + std::iota(std::begin(srcData), std::end(srcData), 1); + + tosa_tensor_t input; + input.shape = input_shape.data(); + input.num_dims = input_shape.size(); + input.data_type = dt; + input.data = reinterpret_cast<uint8_t*>(srcData.data()); + input.size = srcData.size() * sizeof(float); + + tosa_tensor_t output; + output.shape = output_shape.data(); + output.num_dims = output_shape.size(); + output.data_type = dt; + output.data = reinterpret_cast<uint8_t*>(dstData.data()); + output.size = dstData.size() * sizeof(float); + + // Execution + auto status = tosa_run_max_pool2d(input, kernel, stride, pad, 0, 0, output); + CHECK((status == tosa_status_valid)); + + // Compare results + std::vector<float> expectedData = { 6, 8, 14, 16, 22, 24, 30, 32 }; + compareOutput(dstData, expectedData, expectedData.size()); + } - // Read in inputs and expected outputs. - inputs[0] = readFromNpyFile<float>(input0_file.c_str(), input0_shape); - inputs[1] = readFromNpyFile<float>(input1_file.c_str(), input1_shape); - expected_outputs = readFromNpyFile<float>(expected_output_file.c_str(), output_shape); + TEST_CASE("op_entry_pad") + { + // Inputs/Outputs + tosa_datatype_t dt = tosa_datatype_fp32_t; + std::vector<int32_t> input_shape = { 2, 2 }; + std::vector<int32_t> output_shape = { 4, 4 }; + std::vector<float> srcData1(4, 4.0f); + std::vector<float> dstData(16, 0.0f); + + tosa_tensor_t input1; + input1.shape = input_shape.data(); + input1.num_dims = input_shape.size(); + input1.data_type = dt; + input1.data = reinterpret_cast<uint8_t*>(srcData1.data()); + input1.size = srcData1.size() * sizeof(float); + + tosa_tensor_t output; + output.shape = output_shape.data(); + output.num_dims = output_shape.size(); + output.data_type = dt; + output.data = reinterpret_cast<uint8_t*>(dstData.data()); + output.size = dstData.size() * sizeof(float); + + // Execution + int32_t padding[4] = { 1, 1, 1, 1 }; + int32_t padding_len = 4; + int32_t pad_const_int = 0; + float pad_const_fp = 5.0f; + auto status = tosa_run_pad(input1, padding_len, padding, pad_const_int, pad_const_fp, output); + CHECK((status == tosa_status_valid)); + + // Compare results + // Expect a 4x4 array with a border of 5's and inner 2x2 of 4's + std::vector<float> expectedData(16, 5.0f); + expectedData[5] = 4.0f; + expectedData[6] = 4.0f; + expectedData[9] = 4.0f; + expectedData[10] = 4.0f; + compareOutput(dstData, expectedData, expectedData.size()); + } - TosaSerializationHandler handler; - tosa_err_t error = handler.LoadFileTosaFlatbuffer(tosa_model_file.c_str()); - CHECK((error == tosa::TOSA_OK)); + TEST_CASE("simple_add_f32_test") + { + std::string test_root(std::string(PROJECT_ROOT) + "../examples/test_add_1x4x4x4_f32/"); + std::string tosa_model_file(test_root + "flatbuffer-tflite/test_add_1x4x4x4_f32.tosa"); + std::string input0_file(test_root + "placeholder_0.npy"); + std::string input1_file(test_root + "placeholder_1.npy"); + std::string expected_output_file(test_root + "tflite_result.npy"); - GraphStatus status; + std::vector<std::string> input_names = { "TosaInput_0", "TosaInput_1" }; + std::string output_name = "TosaOutput_0"; - // Initialize the ModelRunner with configurations. - IModelRunner runner; - status = runner.initialize(handler); - CHECK((status == GraphStatus::TOSA_VALID)); + std::vector<int32_t> input0_shape = { 1, 4, 4, 1 }; + std::vector<int32_t> input1_shape = { 1, 4, 4, 4 }; + std::vector<int32_t> output_shape = { 1, 4, 4, 4 }; - runner.setInput(input_names[0], inputs[0]); - runner.setInput(input_names[1], inputs[1]); + std::vector<std::vector<float>> inputs(input_names.size()); + std::vector<float> actual_outputs = {}; + std::vector<float> expected_outputs = {}; - // Run the ModelRunner using test inputs. - status = runner.run(); - CHECK((status == GraphStatus::TOSA_VALID)); + // Read in inputs and expected outputs. + inputs[0] = readFromNpyFile<float>(input0_file.c_str(), input0_shape); + inputs[1] = readFromNpyFile<float>(input1_file.c_str(), input1_shape); + expected_outputs = readFromNpyFile<float>(expected_output_file.c_str(), output_shape); - actual_outputs = runner.getOutput<float>(output_name); - CHECK(!actual_outputs.empty()); + TosaSerializationHandler handler; + tosa_err_t error = handler.LoadFileTosaFlatbuffer(tosa_model_file.c_str()); + CHECK((error == tosa::TOSA_OK)); - compareOutput(expected_outputs, actual_outputs, expected_outputs.size()); -} + GraphStatus status; -TEST_CASE("conv2d_f32_test") -{ - std::string test_root(std::string(PROJECT_ROOT) + "../examples/test_conv2d_1x1_1x32x32x8_f32_st11_padSAME_dilat11/"); - std::string tosa_model_file(test_root + "flatbuffer-tflite/test_conv2d_1x1_1x32x32x8_f32_st11_padSAME_dilat11.tosa"); - std::string input_file(test_root + "placeholder_0.npy"); - std::string expected_output_file(test_root + "tflite_result.npy"); + // Initialize the ModelRunner with configurations. + IModelRunner runner; + status = runner.initialize(handler); + CHECK((status == GraphStatus::TOSA_VALID)); - std::string input_name = "TosaInput_0"; - std::string output_name = "TosaOutput_0"; + runner.setInput(input_names[0], inputs[0]); + runner.setInput(input_names[1], inputs[1]); - std::vector<int32_t> input_shape = { 1, 32, 32, 8 }; - std::vector<int32_t> output_shape = { 1, 32, 32, 16 }; + // Run the ModelRunner using test inputs. + status = runner.run(); + CHECK((status == GraphStatus::TOSA_VALID)); - // Read in inputs and expected outputs. - std::vector<float> inputs = readFromNpyFile<float>(input_file.c_str(), input_shape); - std::vector<float> expected_outputs = readFromNpyFile<float>(expected_output_file.c_str(), output_shape); + actual_outputs = runner.getOutput<float>(output_name); + CHECK(!actual_outputs.empty()); - TosaSerializationHandler handler; - tosa_err_t error = handler.LoadFileTosaFlatbuffer(tosa_model_file.c_str()); - CHECK((error == tosa::TOSA_OK)); + compareOutput(expected_outputs, actual_outputs, expected_outputs.size()); + } - GraphStatus status; + TEST_CASE("conv2d_f32_test") + { + std::string test_root(std::string(PROJECT_ROOT) + + "../examples/test_conv2d_1x1_1x32x32x8_f32_st11_padSAME_dilat11/"); + std::string tosa_model_file(test_root + + "flatbuffer-tflite/test_conv2d_1x1_1x32x32x8_f32_st11_padSAME_dilat11.tosa"); + std::string input_file(test_root + "placeholder_0.npy"); + std::string expected_output_file(test_root + "tflite_result.npy"); - // Initialize the ModelRunner with configurations. - IModelRunner runner; - status = runner.initialize(handler); - CHECK((status == GraphStatus::TOSA_VALID)); + std::string input_name = "TosaInput_0"; + std::string output_name = "TosaOutput_0"; - runner.setInput(input_name, inputs); + std::vector<int32_t> input_shape = { 1, 32, 32, 8 }; + std::vector<int32_t> output_shape = { 1, 32, 32, 16 }; - // Run the ModelRunner using test inputs. - status = runner.run(); - CHECK((status == GraphStatus::TOSA_VALID)); + // Read in inputs and expected outputs. + std::vector<float> inputs = readFromNpyFile<float>(input_file.c_str(), input_shape); + std::vector<float> expected_outputs = readFromNpyFile<float>(expected_output_file.c_str(), output_shape); - std::vector<float> actual_outputs = runner.getOutput<float>(output_name); - CHECK(!actual_outputs.empty()); + TosaSerializationHandler handler; + tosa_err_t error = handler.LoadFileTosaFlatbuffer(tosa_model_file.c_str()); + CHECK((error == tosa::TOSA_OK)); - compareOutput(expected_outputs, actual_outputs, expected_outputs.size()); -} + GraphStatus status; -TEST_CASE("conv2d_f32_validate_only_test") -{ - std::string test_root(std::string(PROJECT_ROOT) + "../examples/test_conv2d_1x1_1x32x32x8_f32_st11_padSAME_dilat11/"); - std::string tosa_model_file(test_root + "flatbuffer-tflite/test_conv2d_1x1_1x32x32x8_f32_st11_padSAME_dilat11.tosa"); + // Initialize the ModelRunner with configurations. + IModelRunner runner; + status = runner.initialize(handler); + CHECK((status == GraphStatus::TOSA_VALID)); - TosaSerializationHandler handler; - tosa_err_t error = handler.LoadFileTosaFlatbuffer(tosa_model_file.c_str()); - CHECK((error == tosa::TOSA_OK)); + runner.setInput(input_name, inputs); - GraphStatus status; - func_debug_t funcDebug; + // Run the ModelRunner using test inputs. + status = runner.run(); + CHECK((status == GraphStatus::TOSA_VALID)); - func_config_t funcConfig; - funcConfig.validate_only = 1; + std::vector<float> actual_outputs = runner.getOutput<float>(output_name); + CHECK(!actual_outputs.empty()); - // Initialize the ModelRunner with configurations. - IModelRunner runner = IModelRunner(funcConfig, funcDebug); - runner.setFuncConfig(funcConfig); - status = runner.initialize(handler); - CHECK((status == GraphStatus::TOSA_VALID)); + compareOutput(expected_outputs, actual_outputs, expected_outputs.size()); + } - // Run the ModelRunner using no inputs, as validate_only is specified run() should still work. - status = runner.run(); - CHECK((status == GraphStatus::TOSA_VALID)); -} + TEST_CASE("conv2d_f32_validate_only_test") + { + std::string test_root(std::string(PROJECT_ROOT) + + "../examples/test_conv2d_1x1_1x32x32x8_f32_st11_padSAME_dilat11/"); + std::string tosa_model_file(test_root + + "flatbuffer-tflite/test_conv2d_1x1_1x32x32x8_f32_st11_padSAME_dilat11.tosa"); + + TosaSerializationHandler handler; + tosa_err_t error = handler.LoadFileTosaFlatbuffer(tosa_model_file.c_str()); + CHECK((error == tosa::TOSA_OK)); + + GraphStatus status; + func_debug_t funcDebug; + + func_config_t funcConfig; + funcConfig.validate_only = 1; + + // Initialize the ModelRunner with configurations. + IModelRunner runner = IModelRunner(funcConfig, funcDebug); + runner.setFuncConfig(funcConfig); + status = runner.initialize(handler); + CHECK((status == GraphStatus::TOSA_VALID)); + + // Run the ModelRunner using no inputs, as validate_only is specified run() should still work. + status = runner.run(); + CHECK((status == GraphStatus::TOSA_VALID)); + } -} +} // TEST_SUITE(model_runner) |