diff --git a/include/tim/vx/ops/relational_operations.h b/include/tim/vx/ops/relational_operations.h new file mode 100644 index 0000000..b3997da --- /dev/null +++ b/include/tim/vx/ops/relational_operations.h @@ -0,0 +1,51 @@ +/**************************************************************************** +* +* Copyright (c) 2021 Vivante Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +*****************************************************************************/ +#ifndef TIM_VX_OPS_RELATIONAL_H_ +#define TIM_VX_OPS_RELATIONAL_H_ +#include "tim/vx/operation.h" + +namespace tim { +namespace vx { +namespace ops { + +#define DECLARE_RELATIONAL_OP(NAME) \ + class NAME : public Operation { \ + public: \ + NAME(Graph* graph); \ + }; + +DECLARE_RELATIONAL_OP(Greater) +DECLARE_RELATIONAL_OP(GreaterOrEqual) +DECLARE_RELATIONAL_OP(Less) +DECLARE_RELATIONAL_OP(LessOrEqual) +DECLARE_RELATIONAL_OP(NotEqual) +DECLARE_RELATIONAL_OP(Equal) + +#undef DECLARE_RELATIONAL_OP + +} // namespace ops +} // namespace vx +} // namespace tim + +#endif /* TIM_VX_OPS_RELATIONAL_H_ */ diff --git a/include/tim/vx/types.h b/include/tim/vx/types.h index f149ae2..2cad3fc 100644 --- a/include/tim/vx/types.h +++ b/include/tim/vx/types.h @@ -36,7 +36,8 @@ enum class DataType { INT32, UINT32, FLOAT16, - FLOAT32 + FLOAT32, + BOOL8 }; enum class QuantType { NONE, ASYMMETRIC, SYMMETRIC_PER_CHANNEL }; diff --git a/src/tim/CMakeLists.txt b/src/tim/CMakeLists.txt index b2817f9..3dcfda7 100644 --- a/src/tim/CMakeLists.txt +++ b/src/tim/CMakeLists.txt @@ -16,6 +16,7 @@ list(REMOVE_ITEM SRC ./vx/context_test.cc) list(REMOVE_ITEM SRC ./vx/graph_test.cc) list(REMOVE_ITEM SRC ./vx/ops/reorg_test.cc) list(REMOVE_ITEM SRC ./vx/ops/simple_operations_test.cc) +list(REMOVE_ITEM SRC ./vx/ops/relational_operations_test.cc) include_directories(${PROJECT_SOURCE_DIR}/include) include_directories(${PROJECT_SOURCE_DIR}/include/tim/vx) diff --git a/src/tim/vx/ops/README.md b/src/tim/vx/ops/README.md index 99f7e63..01ca5cf 100644 --- a/src/tim/vx/ops/README.md +++ b/src/tim/vx/ops/README.md @@ -59,7 +59,11 @@ Pad|PAD|Mapped ||MATRIXMUL|Unmapped ||LSTMUNIT|Unmapped ||LAYER_NORM|Unmapped -Min/Max/Any/prod/Mean|REDUCE|Mapped +ReduceMin|REDUCE_MIN|Mapped +ReduceMax|REDUCE_MAX|Mapped +ReduceAny|REDUCE_ANY|Mapped +ReduceProd|REDUCE_PROD|Mapped +ReduceMean|REDUCE_MEAN|Mapped ||INSTANCE_NORM|Unmapped ||TENSORSTACKCONCAT|Unmapped StridedSlice|STRIDED_SLICE|Mapped @@ -71,7 +75,12 @@ Abs|ABS|Mapped NBG|NBG|Mapped ||CONCATSHIFT|Unmapped LocalResponseNormalization|LRN2|Mapped -||RELATIONAL_OPS|Unmapped +Greater|RELATIONAL_OPS_GREATER|Mapped +GreaterOrEqual|RELATIONAL_OPS_GREATER_EQUAL|Mapped +Less|RELATIONAL_OPS_LESS|Mapped +LessOrEqual|RELATIONAL_OPS_LESS_EQUAL|Mapped +Equal|RELATIONAL_OPS_EQUAL|Mapped +NotEqual|RELATIONAL_OPS_NOT_EQUAL|Mapped ||SYNC_HOST|Unmapped Pow|POW|Mapped ||FLOORDIV|Unmapped diff --git a/src/tim/vx/ops/relational_operations.cc b/src/tim/vx/ops/relational_operations.cc new file mode 100644 index 0000000..8b0dc73 --- /dev/null +++ b/src/tim/vx/ops/relational_operations.cc @@ -0,0 +1,49 @@ +/**************************************************************************** +* +* Copyright (c) 2021 Vivante Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +*****************************************************************************/ +#include "tim/vx/ops/relational_operations.h" + +#include "operation_private.h" +#include "vsi_nn_pub.h" + +namespace tim { +namespace vx { +namespace ops { + +#define DEFINE_RELATIONAL_OP(NAME, VSI_OP_CODE) \ + NAME::NAME(Graph* graph) : Operation(graph, VSI_NN_OP_RELATIONAL_OPS, 2, 1) { \ + this->impl()->node()->nn_param.relational_ops.op = VSI_OP_CODE; \ + } + +DEFINE_RELATIONAL_OP(Greater, VSI_NN_RELATIONAL_OPS_GREAT) +DEFINE_RELATIONAL_OP(GreaterOrEqual, VSI_NN_RELATIONAL_OPS_GREAT_EQUAL) +DEFINE_RELATIONAL_OP(Less, VSI_NN_RELATIONAL_OPS_LESS) +DEFINE_RELATIONAL_OP(LessOrEqual, VSI_NN_RELATIONAL_OPS_LESS_EQUAL) +DEFINE_RELATIONAL_OP(NotEqual, VSI_NN_RELATIONAL_OPS_NOT_EQUAL) +DEFINE_RELATIONAL_OP(Equal, VSI_NN_RELATIONAL_OPS_EQUAL) + +#undef DEFINE_RELATIONAL_OP + +} // namespace ops +} // namespace vx +} // namespace tim diff --git a/src/tim/vx/ops/relational_operations_test.cc b/src/tim/vx/ops/relational_operations_test.cc new file mode 100644 index 0000000..2c86722 --- /dev/null +++ b/src/tim/vx/ops/relational_operations_test.cc @@ -0,0 +1,252 @@ +/**************************************************************************** +* +* Copyright (c) 2021 Vivante Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +*****************************************************************************/ +#include "tim/vx/context.h" +#include "tim/vx/graph.h" +#include "tim/vx/ops/relational_operations.h" + +#include "gtest/gtest.h" + +TEST(OP, equal_shape_1_uint8) { + auto ctx = tim::vx::Context::Create(); + auto graph = ctx->CreateGraph(); + + tim::vx::ShapeType io_shape({1}); + tim::vx::Quantization quant(tim::vx::QuantType::ASYMMETRIC, 1, 0); + tim::vx::TensorSpec input_spec(tim::vx::DataType::UINT8, + io_shape, tim::vx::TensorAttribute::INPUT, quant); + tim::vx::TensorSpec output_spec(tim::vx::DataType::BOOL8, + io_shape, tim::vx::TensorAttribute::OUTPUT); + + auto input_tensor1 = graph->CreateTensor(input_spec); + auto input_tensor2 = graph->CreateTensor(input_spec); + auto output_tensor = graph->CreateTensor(output_spec); + + std::vector in_data1 = { 255 }; + std::vector in_data2 = { 0 }; + + std::vector golden = {0}; + + EXPECT_TRUE(input_tensor1->CopyDataToTensor(in_data1.data(), in_data1.size())); + EXPECT_TRUE(input_tensor2->CopyDataToTensor(in_data2.data(), in_data2.size())); + + auto add = graph->CreateOperation(); + (*add).BindInputs({input_tensor1, input_tensor2}).BindOutputs({output_tensor}); + + EXPECT_TRUE(graph->Compile()); + EXPECT_TRUE(graph->Run()); + + //Not using vector because it uses a bitfield representation internally + //and it's cumbersome to copy tensor data to it. + std::vector output(1, 0); + EXPECT_TRUE(output_tensor->CopyDataFromTensor(output.data())); + EXPECT_EQ(golden, output); +} + +TEST(OP, notequal_shape_5_fp32) { + auto ctx = tim::vx::Context::Create(); + auto graph = ctx->CreateGraph(); + + tim::vx::ShapeType io_shape({5}); + tim::vx::TensorSpec input_spec(tim::vx::DataType::FLOAT32, + io_shape, tim::vx::TensorAttribute::INPUT); + tim::vx::TensorSpec output_spec(tim::vx::DataType::BOOL8, + io_shape, tim::vx::TensorAttribute::OUTPUT); + + auto input_tensor1 = graph->CreateTensor(input_spec); + auto input_tensor2 = graph->CreateTensor(input_spec); + auto output_tensor = graph->CreateTensor(output_spec); + + std::vector in_data1 = { -2.5, -0.1, 0, 0.55, std::numeric_limits::infinity() }; + std::vector in_data2 = { -2, -1, 0.2, 0.55, std::numeric_limits::infinity() }; + + std::vector golden = {1, 1, 1, 0, 0}; + + EXPECT_TRUE(input_tensor1->CopyDataToTensor(in_data1.data(), in_data1.size())); + EXPECT_TRUE(input_tensor2->CopyDataToTensor(in_data2.data(), in_data2.size())); + + auto add = graph->CreateOperation(); + (*add).BindInputs({input_tensor1, input_tensor2}).BindOutputs({output_tensor}); + + EXPECT_TRUE(graph->Compile()); + EXPECT_TRUE(graph->Run()); + + //Not using vector because it uses a bitfield representation internally + //and it's cumbersome to copy tensor data to it. + std::vector output(5, 0); + EXPECT_TRUE(output_tensor->CopyDataFromTensor(output.data())); + EXPECT_EQ(golden, output); +} + +TEST(OP, less_shape_5_1_fp32) { + auto ctx = tim::vx::Context::Create(); + auto graph = ctx->CreateGraph(); + + tim::vx::ShapeType io_shape({1,5}); + tim::vx::TensorSpec input_spec(tim::vx::DataType::FLOAT32, + io_shape, tim::vx::TensorAttribute::INPUT); + tim::vx::TensorSpec output_spec(tim::vx::DataType::BOOL8, + io_shape, tim::vx::TensorAttribute::OUTPUT); + + auto input_tensor1 = graph->CreateTensor(input_spec); + auto input_tensor2 = graph->CreateTensor(input_spec); + auto output_tensor = graph->CreateTensor(output_spec); + + std::vector in_data1 = { 0.1, 0.1, 0, 0.55, std::numeric_limits::infinity() }; + std::vector in_data2 = { -1, -1, 0.2, 0.55, std::numeric_limits::infinity() }; + + std::vector golden = {0, 0, 1, 0, 0}; + + EXPECT_TRUE(input_tensor1->CopyDataToTensor(in_data1.data(), in_data1.size())); + EXPECT_TRUE(input_tensor2->CopyDataToTensor(in_data2.data(), in_data2.size())); + + auto add = graph->CreateOperation(); + (*add).BindInputs({input_tensor1, input_tensor2}).BindOutputs({output_tensor}); + + EXPECT_TRUE(graph->Compile()); + EXPECT_TRUE(graph->Run()); + + //Not using vector because it uses a bitfield representation internally + //and it's cumbersome to copy tensor data to it. + std::vector output(5, 0); + EXPECT_TRUE(output_tensor->CopyDataFromTensor(output.data())); + EXPECT_EQ(golden, output); +} + +TEST(OP, greaterorequal_shape_5_2_1_fp32) { + auto ctx = tim::vx::Context::Create(); + auto graph = ctx->CreateGraph(); + + tim::vx::ShapeType io_shape({5,2,1}); + tim::vx::TensorSpec input_spec(tim::vx::DataType::FLOAT32, + io_shape, tim::vx::TensorAttribute::INPUT); + tim::vx::TensorSpec output_spec(tim::vx::DataType::BOOL8, + io_shape, tim::vx::TensorAttribute::OUTPUT); + + auto input_tensor1 = graph->CreateTensor(input_spec); + auto input_tensor2 = graph->CreateTensor(input_spec); + auto output_tensor = graph->CreateTensor(output_spec); + + std::vector in_data1 = { + -2.5, -0.1, 0, 0.55, std::numeric_limits::infinity(), + -2.5, -0.1, 0, 0.55, std::numeric_limits::infinity() }; + std::vector in_data2 = { + -2, -1, 0.2, 0.55, std::numeric_limits::infinity(), + -2, -1, 0.2, 0.55, std::numeric_limits::infinity() }; + + std::vector golden = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1}; + + EXPECT_TRUE(input_tensor1->CopyDataToTensor(in_data1.data(), in_data1.size())); + EXPECT_TRUE(input_tensor2->CopyDataToTensor(in_data2.data(), in_data2.size())); + + auto add = graph->CreateOperation(); + (*add).BindInputs({input_tensor1, input_tensor2}).BindOutputs({output_tensor}); + + EXPECT_TRUE(graph->Compile()); + EXPECT_TRUE(graph->Run()); + + //Not using vector because it uses a bitfield representation internally + //and it's cumbersome to copy tensor data to it. + std::vector output(10, 0); + EXPECT_TRUE(output_tensor->CopyDataFromTensor(output.data())); + EXPECT_EQ(golden, output); +} + +TEST(OP, greater_shape_5_2_1_1_fp32) { + auto ctx = tim::vx::Context::Create(); + auto graph = ctx->CreateGraph(); + + tim::vx::ShapeType io_shape({5,2,1,1}); + tim::vx::TensorSpec input_spec(tim::vx::DataType::FLOAT32, + io_shape, tim::vx::TensorAttribute::INPUT); + tim::vx::TensorSpec output_spec(tim::vx::DataType::BOOL8, + io_shape, tim::vx::TensorAttribute::OUTPUT); + + auto input_tensor1 = graph->CreateTensor(input_spec); + auto input_tensor2 = graph->CreateTensor(input_spec); + auto output_tensor = graph->CreateTensor(output_spec); + + std::vector in_data1 = { + -2.5, -0.1, 0, 0.55, std::numeric_limits::infinity(), + -2.5, -0.1, 0, 0.55, std::numeric_limits::infinity() }; + std::vector in_data2 = { + -2, -1, 0.2, 0.55, std::numeric_limits::infinity(), + -2, -1, 0.2, 0.55, std::numeric_limits::infinity() }; + + std::vector golden = {0, 1, 0, 0, 0, 0, 1, 0, 0, 0}; + + EXPECT_TRUE(input_tensor1->CopyDataToTensor(in_data1.data(), in_data1.size())); + EXPECT_TRUE(input_tensor2->CopyDataToTensor(in_data2.data(), in_data2.size())); + + auto add = graph->CreateOperation(); + (*add).BindInputs({input_tensor1, input_tensor2}).BindOutputs({output_tensor}); + + EXPECT_TRUE(graph->Compile()); + EXPECT_TRUE(graph->Run()); + + //Not using vector because it uses a bitfield representation internally + //and it's cumbersome to copy tensor data to it. + std::vector output(10, 0); + EXPECT_TRUE(output_tensor->CopyDataFromTensor(output.data())); + EXPECT_EQ(golden, output); +} + +TEST(OP, lessorequal_shape_1_5_2_1_1_fp32) { + auto ctx = tim::vx::Context::Create(); + auto graph = ctx->CreateGraph(); + + tim::vx::ShapeType io_shape({1,5,2,1,1}); + tim::vx::TensorSpec input_spec(tim::vx::DataType::FLOAT32, + io_shape, tim::vx::TensorAttribute::INPUT); + tim::vx::TensorSpec output_spec(tim::vx::DataType::BOOL8, + io_shape, tim::vx::TensorAttribute::OUTPUT); + + auto input_tensor1 = graph->CreateTensor(input_spec); + auto input_tensor2 = graph->CreateTensor(input_spec); + auto output_tensor = graph->CreateTensor(output_spec); + + std::vector in_data1 = { + -2.5, -0.1, 0, 0.55, std::numeric_limits::infinity(), + -2.5, -0.1, 0, 0.55, std::numeric_limits::infinity() }; + std::vector in_data2 = { + -2, -1, 0.2, 0.55, std::numeric_limits::infinity(), + -2, -1, 0.2, 0.55, std::numeric_limits::infinity() }; + + std::vector golden = {1, 0, 1, 1, 1, 1, 0, 1, 1, 1}; + + EXPECT_TRUE(input_tensor1->CopyDataToTensor(in_data1.data(), in_data1.size())); + EXPECT_TRUE(input_tensor2->CopyDataToTensor(in_data2.data(), in_data2.size())); + + auto add = graph->CreateOperation(); + (*add).BindInputs({input_tensor1, input_tensor2}).BindOutputs({output_tensor}); + + EXPECT_TRUE(graph->Compile()); + EXPECT_TRUE(graph->Run()); + + //Not using vector because it uses a bitfield representation internally + //and it's cumbersome to copy tensor data to it. + std::vector output(10, 0); + EXPECT_TRUE(output_tensor->CopyDataFromTensor(output.data())); + EXPECT_EQ(golden, output); +} + diff --git a/src/tim/vx/type_utils.cc b/src/tim/vx/type_utils.cc index 945f23a..12e4991 100644 --- a/src/tim/vx/type_utils.cc +++ b/src/tim/vx/type_utils.cc @@ -43,6 +43,8 @@ vsi_nn_type_e TranslateDataType(DataType dtype) { return VSI_NN_TYPE_FLOAT16; case DataType::FLOAT32: return VSI_NN_TYPE_FLOAT32; + case DataType::BOOL8: + return VSI_NN_TYPE_BOOL8; default: break; }