diff options
Diffstat (limited to 'tests/nnfw_api/src/one_op_tests/While.test.cc')
-rw-r--r-- | tests/nnfw_api/src/one_op_tests/While.test.cc | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/tests/nnfw_api/src/one_op_tests/While.test.cc b/tests/nnfw_api/src/one_op_tests/While.test.cc new file mode 100644 index 000000000..5c4da552c --- /dev/null +++ b/tests/nnfw_api/src/one_op_tests/While.test.cc @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GenModelTest.h" +#include "WhileTestModel.h" + +#include <memory> + +TEST_F(GenModelTest, OneOp_While) +{ + WhileModelLoop10 model; + _context = std::make_unique<GenModelTestContext>(std::move(model.cbuf)); + _context->addTestCase(uniformTCD<float>({{0}}, {{100}})); + _context->addTestCase(uniformTCD<float>({{2}}, {{102}})); + _context->addTestCase(uniformTCD<float>({{22}}, {{102}})); + _context->addTestCase(uniformTCD<float>({{100}}, {{100}})); + _context->setBackends({"cpu"}); + + SUCCEED(); +} + +TEST_F(GenModelTest, OneOp_While_github_4783) +{ + // The model looks just like the below pseudocode + // + // function model(x, data) + // { + // // `data` does not do anything but passed to while's cond and body subgraphs + // // to measure copy overhead between subgraphs + // while (x < 100.0) + // { + // x = x + 1.0; + // } + // return (x, data) + // } + + const int kElems = 4; + const std::vector<int32_t> shape{kElems}; + + CircleGen cgen; + uint32_t incr_buf = cgen.addBuffer(std::vector<float>{1}); + uint32_t incr_data_buf = cgen.addBuffer(std::vector<float>(kElems, 1)); + uint32_t end_buf = cgen.addBuffer(std::vector<float>{100}); + + // primary subgraph + { + int x_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int d_in = cgen.addTensor({shape, circle::TensorType_FLOAT32}); + int x_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int d_out = cgen.addTensor({shape, circle::TensorType_FLOAT32}); + cgen.addOperatorWhile({{x_in, d_in}, {x_out, d_out}}, 1, 2); + cgen.setInputsAndOutputs({x_in, d_in}, {x_out, d_out}); + } + + // cond subgraph + { + cgen.nextSubgraph(); + int x = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int d = cgen.addTensor({shape, circle::TensorType_FLOAT32}); + int end = cgen.addTensor({{1}, circle::TensorType_FLOAT32, end_buf}); + int result = cgen.addTensor({{1}, circle::TensorType_BOOL}); + cgen.addOperatorLess({{x, end}, {result}}); + cgen.setInputsAndOutputs({x, d}, {result}); + } + + // body subgraph + { + cgen.nextSubgraph(); + int x_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int incr = cgen.addTensor({{1}, circle::TensorType_FLOAT32, incr_buf}); + int x_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int d_in = cgen.addTensor({shape, circle::TensorType_FLOAT32}); + int incr_d = cgen.addTensor({shape, circle::TensorType_FLOAT32, incr_data_buf}); + int d_out = cgen.addTensor({shape, circle::TensorType_FLOAT32}); + cgen.addOperatorAdd({{x_in, incr}, {x_out}}, circle::ActivationFunctionType_NONE); + cgen.addOperatorAdd({{d_in, incr_d}, {d_out}}, circle::ActivationFunctionType_NONE); + cgen.setInputsAndOutputs({x_in, d_in}, {x_out, d_out}); + } + + _context = std::make_unique<GenModelTestContext>(cgen.finish()); + std::vector<float> tc_data_in(kElems, 9); + std::vector<float> tc_data_out(kElems, 109); + _context->addTestCase(uniformTCD<float>({{0}, tc_data_in}, {{100}, tc_data_out})); + _context->setBackends({"cpu"}); + + SUCCEED(); +} + +TEST_F(GenModelTest, OneOp_While_TwoInputs) +{ + // The model looks just like the below pseudocode + // + // function model(x, end) + // { + // while (x < end) + // { + // x = x + 10.0 + // } + // return x + // } + + CircleGen cgen; + std::vector<float> incr_data{10}; + uint32_t incr_buf = cgen.addBuffer(incr_data); + + // primary subgraph + { + int x_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int x_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + cgen.addOperatorWhile({{x_in, end_in}, {x_out, end_out}}, 1, 2); + cgen.setInputsAndOutputs({x_in, end_in}, {x_out}); + } + + // cond subgraph + { + cgen.nextSubgraph(); + int x = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int result = cgen.addTensor({{1}, circle::TensorType_BOOL}); + cgen.addOperatorLess({{x, end}, {result}}); + cgen.setInputsAndOutputs({x, end}, {result}); + } + + // body subgraph + { + cgen.nextSubgraph(); + int x_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int incr = cgen.addTensor({{1}, circle::TensorType_FLOAT32, incr_buf}); + int x_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + cgen.addOperatorAdd({{x_in, incr}, {x_out}}, circle::ActivationFunctionType_NONE); + cgen.setInputsAndOutputs({x_in, end}, {x_out, end}); + } + + _context = std::make_unique<GenModelTestContext>(cgen.finish()); + _context->addTestCase(uniformTCD<float>({{0}, {20}}, {{20}})); + _context->addTestCase(uniformTCD<float>({{5}, {30}}, {{35}})); + _context->addTestCase(uniformTCD<float>({{20}, {10}}, {{20}})); + _context->setBackends({"cpu"}); + + SUCCEED(); +} + +class WhileWrongSubgraphIndex : public GenModelTest, + public ::testing::WithParamInterface<std::pair<int, int>> +{ +}; + +TEST_P(WhileWrongSubgraphIndex, neg_Test) +{ + // These values must be less than 0 or greater than 2 + int cond_subg = GetParam().first; + int body_subg = GetParam().second; + + // When While operation's subgraph index is invalid + + CircleGen cgen; + + // constant buffers + std::vector<float> incr_data{10}; + uint32_t incr_buf = cgen.addBuffer(incr_data); + + // primary subgraph + { + int x_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int x_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + cgen.addOperatorWhile({{x_in, end_in}, {x_out, end_out}}, cond_subg, body_subg); + cgen.setInputsAndOutputs({x_in, end_in}, {x_out}); + } + + // cond subgraph + { + cgen.nextSubgraph(); + int x = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int result = cgen.addTensor({{1}, circle::TensorType_BOOL}); + cgen.addOperatorLess({{x, end}, {result}}); + cgen.setInputsAndOutputs({x, end}, {result}); + } + + // body subgraph + { + cgen.nextSubgraph(); + int x_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int incr = cgen.addTensor({{1}, circle::TensorType_FLOAT32, incr_buf}); + int x_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + cgen.addOperatorAdd({{x_in, incr}, {x_out}}, circle::ActivationFunctionType_NONE); + cgen.setInputsAndOutputs({x_in, end}, {x_out, end}); + } + + _context = std::make_unique<GenModelTestContext>(cgen.finish()); + _context->setBackends({"cpu"}); + _context->expectFailModelLoad(); + + SUCCEED(); +} + +INSTANTIATE_TEST_SUITE_P(GenModelTest, WhileWrongSubgraphIndex, + ::testing::Values(std::make_pair(99, 2), std::make_pair(-1, 2), + std::make_pair(1, 99), std::make_pair(1, -99), + std::make_pair(-99, 99))); + +// In this test, output of WHILE and body subgraph have different data types +TEST_F(GenModelTest, neg_while_wrong_dtype) +{ + CircleGen cgen; + std::vector<float> incr_data{10}; + uint32_t incr_buf = cgen.addBuffer(incr_data); + std::vector<float> end_data{100}; + uint32_t end_buf = cgen.addBuffer(end_data); + + // primary subgraph + { + int model_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int model_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + + cgen.addOperatorWhile({{model_in}, {model_out}}, 1, 2); + cgen.setInputsAndOutputs({model_in}, {model_out}); + } + + // cond subgraph + { + cgen.nextSubgraph(); + int x = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int end = cgen.addTensor({{1}, circle::TensorType_FLOAT32, end_buf}); + int result = cgen.addTensor({{1}, circle::TensorType_BOOL}); + cgen.addOperatorLess({{x, end}, {result}}); + cgen.setInputsAndOutputs({x}, {result}); + } + + // body subgraph + { + cgen.nextSubgraph(); + int x_in = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int incr = cgen.addTensor({{1}, circle::TensorType_FLOAT32, incr_buf}); + int x_out = cgen.addTensor({{1}, circle::TensorType_FLOAT32}); + int cast_out = cgen.addTensor({{1}, circle::TensorType_INT32}); + cgen.addOperatorAdd({{x_in, incr}, {x_out}}, circle::ActivationFunctionType_NONE); + cgen.addOperatorCast({{x_out}, {cast_out}}, circle::TensorType_FLOAT32, + circle::TensorType_INT32); + cgen.setInputsAndOutputs({x_in}, {cast_out}); + // output of this subgraph is INT32 but output of WHILE is FLOAT32 + } + + _context = std::make_unique<GenModelTestContext>(cgen.finish()); + _context->setBackends({"cpu"}); + // It is correct to call `_context->expectFailModelLoad();`, but OperationValidator does not deal + // with subgraphs. So it is verified by `_context->expectFailCompile(); as a workaround` + _context->expectFailCompile(); + + SUCCEED(); +} |