diff options
author | Anthony Barbier <anthony.barbier@arm.com> | 2017-09-04 18:44:23 +0100 |
---|---|---|
committer | Anthony Barbier <anthony.barbier@arm.com> | 2018-09-17 13:03:09 +0100 |
commit | 6ff3b19ee6120edf015fad8caab2991faa3070af (patch) | |
tree | a7a6dcd16dfd56d79fa1b56a313caeebcc939b68 /tests/validation | |
download | ComputeLibrary-6ff3b19ee6120edf015fad8caab2991faa3070af.tar.gz |
COMPMID-344 Updated doxygen
Change-Id: I32f7b84daa560e460b77216add529c8fa8b327ae
Diffstat (limited to 'tests/validation')
64 files changed, 12950 insertions, 0 deletions
diff --git a/tests/validation/CL/BitwiseAnd.cpp b/tests/validation/CL/BitwiseAnd.cpp new file mode 100644 index 0000000000..4cd64a2a99 --- /dev/null +++ b/tests/validation/CL/BitwiseAnd.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "CL/CLAccessor.h" +#include "CL/Helper.h" +#include "Globals.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLSubTensor.h" +#include "arm_compute/runtime/CL/CLTensor.h" +#include "arm_compute/runtime/CL/CLTensorAllocator.h" +#include "arm_compute/runtime/CL/functions/CLBitwiseAnd.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cl; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon bitwise and function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +CLTensor compute_bitwise_and(const TensorShape &shape) +{ + // Create tensors + CLTensor src1 = create_tensor(shape, DataType::U8); + CLTensor src2 = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + // Create and configure function + CLBitwiseAnd band; + band.configure(&src1, &src2, &dst); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(CLAccessor(src1), 0); + library->fill_tensor_uniform(CLAccessor(src2), 1); + + // Compute function + band.run(); + + return dst; +} + +/** Compute OpenCL bitwise and function that splits the input and output in two subtensor. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +CLTensor compute_bitwise_and_subtensor(const TensorShape &shape) +{ + // Create tensors + CLTensor src1 = create_tensor(shape, DataType::U8); + CLTensor src2 = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + // Create SubTensors + int coord_z = shape.z() / 2; + TensorShape sub_shape = shape; + sub_shape.set(2, coord_z); + + CLSubTensor src1_sub1(&src1, sub_shape, Coordinates()); + CLSubTensor src1_sub2(&src1, sub_shape, Coordinates(0, 0, coord_z)); + CLSubTensor src2_sub1(&src2, sub_shape, Coordinates()); + CLSubTensor src2_sub2(&src2, sub_shape, Coordinates(0, 0, coord_z)); + CLSubTensor dst_sub1(&dst, sub_shape, Coordinates()); + CLSubTensor dst_sub2(&dst, sub_shape, Coordinates(0, 0, coord_z)); + + // Create and configure function + CLBitwiseAnd band1, band2; + band1.configure(&src1_sub1, &src2_sub1, &dst_sub1); + band2.configure(&src1_sub2, &src2_sub2, &dst_sub2); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + std::uniform_int_distribution<> distribution(0, 255); + library->fill(CLAccessor(src1), distribution, 0); + library->fill(CLAccessor(src2), distribution, 1); + + // Compute function + band1.run(); + band2.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(CL) +BOOST_AUTO_TEST_SUITE(BitwiseAnd) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, SmallShapes() + LargeShapes(), shape) +{ + // Create tensors + CLTensor src1 = create_tensor(shape, DataType::U8); + CLTensor src2 = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + CLBitwiseAnd band; + band.configure(&src1, &src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(src2.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + CLTensor dst = compute_bitwise_and(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_and(shape); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_AUTO_TEST_CASE(RunSubTensor) +{ + // Create shape + TensorShape shape(27U, 35U, 8U, 2U); + + // Compute function + CLTensor dst = compute_bitwise_and_subtensor(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_and(shape); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), shape) +{ + // Compute function + CLTensor dst = compute_bitwise_and(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_and(shape); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/CL/CLFixture.cpp b/tests/validation/CL/CLFixture.cpp new file mode 100644 index 0000000000..845e16629d --- /dev/null +++ b/tests/validation/CL/CLFixture.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "validation/CL/CLFixture.h" + +#include "boost_wrapper.h" + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::validation; +using namespace arm_compute::test::validation::cl; + +BOOST_GLOBAL_FIXTURE(CLFixture); diff --git a/tests/validation/CL/CLFixture.h b/tests/validation/CL/CLFixture.h new file mode 100644 index 0000000000..138e0566eb --- /dev/null +++ b/tests/validation/CL/CLFixture.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_VALIDATION_CL_CLFIXTURE_H__ +#define __ARM_COMPUTE_TEST_VALIDATION_CL_CLFIXTURE_H__ + +#include "arm_compute/runtime/CL/CLScheduler.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace cl +{ +struct CLFixture +{ + CLFixture() + { + CLScheduler::get().default_init(); + } +}; +} // namespace cl +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif diff --git a/tests/validation/CL/CMakeLists.txt b/tests/validation/CL/CMakeLists.txt new file mode 100644 index 0000000000..209b662033 --- /dev/null +++ b/tests/validation/CL/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright (c) 2017 ARM Limited. +# +# SPDX-License-Identifier: MIT +# +# 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. +cmake_minimum_required (VERSION 3.1) + +include_directories(${CMAKE_SOURCE_DIR}/../include) + +set(arm_compute_test_validation_OPENCL_SOURCE_FILES + ${CMAKE_SOURCE_DIR}/CL/CLAccessor.h + ${CMAKE_SOURCE_DIR}/CL/Helper.h + ${CMAKE_CURRENT_SOURCE_DIR}/CLFixture.h + ${CMAKE_CURRENT_SOURCE_DIR}/CLFixture.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BitwiseAnd.cpp +) + +add_library(arm_compute_test_validation_OPENCL OBJECT + ${arm_compute_test_validation_OPENCL_SOURCE_FILES} +) + +set(arm_compute_test_validation_TARGET_OBJECTS + ${arm_compute_test_validation_TARGET_OBJECTS} + $<TARGET_OBJECTS:arm_compute_test_validation_OPENCL> + PARENT_SCOPE +) + +set(arm_compute_test_validation_TARGET_LIBRARIES + ${arm_compute_test_validation_TARGET_LIBRARIES} + OpenCL + PARENT_SCOPE +) diff --git a/tests/validation/CL/DepthConvert.cpp b/tests/validation/CL/DepthConvert.cpp new file mode 100644 index 0000000000..7a421ecf5a --- /dev/null +++ b/tests/validation/CL/DepthConvert.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "CL/CLAccessor.h" +#include "CL/Helper.h" +#include "Globals.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/functions/CLDepthConvert.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cl; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute CL depth convert function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in Data type of input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] policy Conversion policy. + * @param[in] shift Value for down/up conversions. Must be 0 <= shift < 8. + * + * @return Computed output CLtensor. + */ +CLTensor compute_depth_convert(const TensorShape &shape, DataType dt_in, DataType dt_out, ConvertPolicy policy, uint32_t shift) +{ + // Create tensors + CLTensor src = create_tensor(shape, dt_in); + CLTensor dst = create_tensor(shape, dt_out); + + // Create and configure function + CLDepthConvert depth_convert; + depth_convert.configure(&src, &dst, policy, shift); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(CLAccessor(src), 0); + + // Compute function + depth_convert.run(); + + return dst; +} +/** Configure and validate region/padding function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in Data type of input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] policy Conversion policy. + * @param[in] shift Value for down/up conversions. Must be 0 <= shift < 8. + * + */ +void compute_configure_validate(const TensorShape &shape, DataType dt_in, DataType dt_out, ConvertPolicy policy, uint32_t shift) +{ + // Create tensors + CLTensor src = create_tensor(shape, dt_in); + CLTensor dst = create_tensor(shape, dt_out); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + CLDepthConvert depth_convert; + depth_convert.configure(&src, &dst, policy, shift); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(CL) +BOOST_AUTO_TEST_SUITE(DepthConvert) + +BOOST_AUTO_TEST_SUITE(U8_to_U16) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U8, DataType::U16, policy, shift); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U8, DataType::U16, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::U16, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U8, DataType::U16, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::U16, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U8_to_S16) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U8, DataType::S16, policy, shift); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U8, DataType::S16, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::S16, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U8, DataType::S16, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::S16, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U8_to_S32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U8, DataType::S32, policy, shift); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U8, DataType::S32, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::S32, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U8, DataType::S32, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::S32, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U16_to_U8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U16, DataType::U8, policy, shift); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U16, DataType::U8, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U16, DataType::U8, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U16, DataType::U8, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U16, DataType::U8, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U16_to_U32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U16, DataType::U32, policy, shift); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U16, DataType::U32, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U16, DataType::U32, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::U16, DataType::U32, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U16, DataType::U32, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(S16_to_U8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::S16, DataType::U8, policy, shift); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::S16, DataType::U8, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::S16, DataType::U8, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::S16, DataType::U8, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::S16, DataType::U8, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(S16_to_S32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::S16, DataType::S32, policy, shift); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::S16, DataType::S32, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::S16, DataType::S32, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + CLTensor dst = compute_depth_convert(shape, DataType::S16, DataType::S32, policy, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::S16, DataType::S32, policy, shift, 0); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/CL/FillBorder.cpp b/tests/validation/CL/FillBorder.cpp new file mode 100644 index 0000000000..42b9064982 --- /dev/null +++ b/tests/validation/CL/FillBorder.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "CL/CLAccessor.h" +#include "CL/Helper.h" +#include "Globals.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Validation.h" + +#include "arm_compute/core/CL/kernels/CLFillBorderKernel.h" +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLScheduler.h" +#include "arm_compute/runtime/CL/CLTensor.h" +#include "arm_compute/runtime/CL/CLTensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cl; +using namespace arm_compute::test::validation; + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(CL) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(FillBorder, BorderModes() * boost::unit_test::data::make({ PaddingSize{ 0 }, PaddingSize{ 1, 0, 1, 2 }, PaddingSize{ 10 } }), border_mode, padding) +{ + constexpr uint8_t border_value = 42U; + constexpr uint8_t tensor_value = 89U; + BorderSize border_size{ 5 }; + + // Create tensors + CLTensor src = create_tensor(TensorShape{ 10U, 10U, 2U }, DataType::U8); + + src.info()->extend_padding(padding); + + // Allocate tensor + src.allocator()->allocate(); + + // Check padding is as required + validate(src.info()->padding(), padding); + + // Fill tensor with constant value + std::uniform_int_distribution<uint8_t> distribution{ tensor_value, tensor_value }; + library->fill(CLAccessor(src), distribution, 0); + + // Create and configure kernel + CLFillBorderKernel fill_border; + fill_border.configure(&src, border_size, border_mode, border_value); + + // Run kernel + fill_border.run(fill_border.window(), CLScheduler::get().queue()); + + // Validate border + border_size.limit(padding); + validate(CLAccessor(src), border_size, border_mode, &border_value); + + // Validate tensor + validate(CLAccessor(src), &tensor_value); +} + +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/CL/Threshold.cpp b/tests/validation/CL/Threshold.cpp new file mode 100644 index 0000000000..a8c77ec10a --- /dev/null +++ b/tests/validation/CL/Threshold.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "CL/CLAccessor.h" +#include "CL/Helper.h" +#include "Globals.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "dataset/ThresholdDataset.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLTensor.h" +#include "arm_compute/runtime/CL/CLTensorAllocator.h" +#include "arm_compute/runtime/CL/functions/CLThreshold.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cl; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Threshold function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] threshold Threshold. When the threshold type is RANGE, this is used as the lower threshold. + * @param[in] false_value value to set when the condition is not respected. + * @param[in] true_value value to set when the condition is respected. + * @param[in] type Thresholding type. Either RANGE or BINARY. + * @param[in] upper Upper threshold. Only used when the thresholding type is RANGE. + * + * @return Computed output tensor. + */ +CLTensor compute_threshold(const TensorShape &shape, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper) +{ + // Create tensors + CLTensor src = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + // Create and configure function + CLThreshold thrsh; + thrsh.configure(&src, &dst, threshold, false_value, true_value, type, upper); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(CLAccessor(src), 0); + + // Compute function + thrsh.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(CL) +BOOST_AUTO_TEST_SUITE(Threshold) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, + (SmallShapes() + LargeShapes()) * ThresholdDataset(), + shape, threshold_conf) +{ + // Create tensors + CLTensor src = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + CLThreshold cl_threshold; + cl_threshold.configure(&src, &dst, threshold_conf.threshold, threshold_conf.false_value, threshold_conf.true_value, threshold_conf.type, threshold_conf.upper); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, + SmallShapes() * ThresholdDataset(), + shape, threshold_conf) +{ + // Compute function + CLTensor dst = compute_threshold(shape, threshold_conf.threshold, threshold_conf.false_value, threshold_conf.true_value, threshold_conf.type, threshold_conf.upper); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_threshold(shape, threshold_conf.threshold, threshold_conf.false_value, threshold_conf.true_value, threshold_conf.type, threshold_conf.upper); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, + LargeShapes() * ThresholdDataset(), + shape, threshold_conf) +{ + // Compute function + CLTensor dst = compute_threshold(shape, threshold_conf.threshold, threshold_conf.false_value, threshold_conf.true_value, threshold_conf.type, threshold_conf.upper); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_threshold(shape, threshold_conf.threshold, threshold_conf.false_value, threshold_conf.true_value, threshold_conf.type, threshold_conf.upper); + + // Validate output + validate(CLAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/CMakeLists.txt b/tests/validation/CMakeLists.txt new file mode 100644 index 0000000000..3d8f56610b --- /dev/null +++ b/tests/validation/CMakeLists.txt @@ -0,0 +1,96 @@ +# Copyright (c) 2017 ARM Limited. +# +# SPDX-License-Identifier: MIT +# +# 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. +cmake_minimum_required (VERSION 3.1) + +add_library(openvx SHARED IMPORTED) +set_target_properties(openvx PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/../3rdparty/linux/armv7a/libopenvx.so" +) + +add_library(vxu SHARED IMPORTED) +set_target_properties(vxu PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/../3rdparty/linux/armv7a/libvxu.so" +) + +add_library(OpenCL SHARED IMPORTED) +set_target_properties(OpenCL PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/../build/opencl-1.2-stubs/libOpenCL.so" + IMPORTED_NO_SONAME 1 +) + +add_definitions(-DBOOST) + +set(ARM_COMPUTE_TARGETS_TO_VALIDATE "all" CACHE STRING "Semicolon-separated list of targets to include in validation.") + +set(ARM_COMPUTE_ALL_TARGETS + NEON + CL + UNIT + VX +) + +if(ARM_COMPUTE_TARGETS_TO_VALIDATE STREQUAL "all") + set(ARM_COMPUTE_TARGETS_TO_VALIDATE ${ARM_COMPUTE_ALL_TARGETS}) +endif() + +list(REMOVE_DUPLICATES ARM_COMPUTE_TARGETS_TO_VALIDATE) + +foreach(TARGET ${ARM_COMPUTE_TARGETS_TO_VALIDATE}) + list(FIND ARM_COMPUTE_ALL_TARGETS ${TARGET} idx) + + if(${idx} LESS 0) + message(FATAL_ERROR "The target '${TARGET}' does not exist. It should be one of\n${ARM_COMPUTE_ALL_TARGETS}") + else() + add_subdirectory(${TARGET}) + endif() +endforeach() + +set(arm_compute_test_validation_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Datasets.h + ${CMAKE_CURRENT_SOURCE_DIR}/Reference.h + ${CMAKE_CURRENT_SOURCE_DIR}/Reference.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ReferenceCPP.h + ${CMAKE_CURRENT_SOURCE_DIR}/ReferenceCPP.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Validation.h + ${CMAKE_CURRENT_SOURCE_DIR}/Validation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ValidationProgramOptions.h + ${CMAKE_CURRENT_SOURCE_DIR}/ValidationUserConfiguration.h +) + +add_library(arm_compute_test_validation OBJECT + ${arm_compute_test_validation_SOURCE_FILES} +) + +add_executable(arm_compute_validation + $<TARGET_OBJECTS:arm_compute_test_validation> + ${arm_compute_test_validation_TARGET_OBJECTS} + $<TARGET_OBJECTS:tensor_library> + $<TARGET_OBJECTS:arm_compute_test> +) + +target_link_libraries(arm_compute_validation + boost_unit_test_framework + boost_program_options + arm_compute + ${arm_compute_test_validation_TARGET_LIBRARIES} +) diff --git a/tests/validation/Datasets.h b/tests/validation/Datasets.h new file mode 100644 index 0000000000..ae76fb6be3 --- /dev/null +++ b/tests/validation/Datasets.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_VALIDATION_DATASETS_H__ +#define __ARM_COMPUTE_TEST_VALIDATION_DATASETS_H__ + +#include "dataset/ActivationFunctionDataset.h" +#include "dataset/BatchNormalizationLayerDataset.h" +#include "dataset/BorderModeDataset.h" +#include "dataset/ConvertPolicyDataset.h" +#include "dataset/ConvolutionLayerDataset.h" +#include "dataset/DataTypeDatasets.h" +#include "dataset/FullyConnectedLayerDataset.h" +#include "dataset/GEMMDataset.h" +#include "dataset/ImageDatasets.h" +#include "dataset/InterpolationPolicyDataset.h" +#include "dataset/NormalizationTypeDataset.h" +#include "dataset/PoolingLayerDataset.h" +#include "dataset/RoundingPolicyDataset.h" +#include "dataset/ShapeDatasets.h" +#include "dataset/ThresholdDataset.h" + +#include "boost_wrapper.h" + +using namespace boost::unit_test::data::monomorphic; + +namespace boost +{ +namespace unit_test +{ +namespace data +{ +namespace monomorphic +{ +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::SmallImages> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::LargeImages> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::SmallShapes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::Small1DShape> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::LargeShapes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::AllDataTypes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::UnsignedDataTypes> : boost::mpl::true_ +{ +}; + +// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::SignedDataTypes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::FloatDataTypes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::FixedPointDataTypes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::CNNFloatDataTypes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::CNNFixedPointDataTypes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::CNNDataTypes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::ActivationFunctions> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::BorderModes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::ConvertPolicies> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::InterpolationPolicies> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::NormalizationTypes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::RandomPoolingLayerDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::RoundingPolicies> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::AlexNetConvolutionLayerDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::AlexNetFullyConnectedLayerDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::DirectConvolutionShapes> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::SmallFullyConnectedLayerDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::LargeFullyConnectedLayerDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::SmallConvolutionLayerDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::SmallGEMMDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::LargeGEMMDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::RandomBatchNormalizationLayerDataset> : boost::mpl::true_ +{ +}; + +/// Register the data set with Boost +template <> +struct is_dataset<arm_compute::test::ThresholdDataset> : boost::mpl::true_ +{ +}; +} +} +} +} +#endif diff --git a/tests/validation/FixedPoint.h b/tests/validation/FixedPoint.h new file mode 100644 index 0000000000..380bad04a1 --- /dev/null +++ b/tests/validation/FixedPoint.h @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_VALIDATION_FIXEDPOINT_H__ +#define __ARM_COMPUTE_TEST_VALIDATION_FIXEDPOINT_H__ + +#include "Utils.h" + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <limits> +#include <string> +#include <type_traits> + +namespace arm_compute +{ +namespace test +{ +namespace fixed_point_arithmetic +{ +namespace detail +{ +// Forward declare structs +struct functions; +template <typename T> +struct constant_expr; +} + +/** Fixed point traits */ +namespace traits +{ +// Promote types +// *INDENT-OFF* +// clang-format off +template <typename T> struct promote { }; +template <> struct promote<uint8_t> { using type = uint16_t; }; +template <> struct promote<int8_t> { using type = int16_t; }; +template <> struct promote<uint16_t> { using type = uint32_t; }; +template <> struct promote<int16_t> { using type = int32_t; }; +template <> struct promote<uint32_t> { using type = uint64_t; }; +template <> struct promote<int32_t> { using type = int64_t; }; +template <> struct promote<uint64_t> { using type = uint64_t; }; +template <> struct promote<int64_t> { using type = int64_t; }; +// clang-format on +// *INDENT-ON* +} + +/** Strongly typed enum class representing the overflow policy */ +enum class OverflowPolicy +{ + WRAP, /**< Wrap policy */ + SATURATE /**< Saturate policy */ +}; +/** Strongly typed enum class representing the rounding policy */ +enum class RoundingPolicy +{ + TO_ZERO, /**< Round to zero policy */ + TO_NEAREST_EVEN /**< Round to nearest even policy */ +}; + +/** Arbitrary fixed-point arithmetic class */ +template <typename T> +class fixed_point +{ +public: + // Static Checks + static_assert(std::is_integral<T>::value, "Type is not an integer"); + + // Friends + friend struct detail::functions; + friend struct detail::constant_expr<T>; + + /** Constructor (from different fixed point type) + * + * @param[in] val Fixed point + * @param[in] p Fixed point precision + */ + template <typename U> + fixed_point(fixed_point<U> val, uint8_t p) + : _value(0), _fixed_point_position(p) + { + assert(p > 0 && p < std::numeric_limits<T>::digits); + T v = 0; + + if(std::numeric_limits<T>::digits < std::numeric_limits<U>::digits) + { + val.rescale(p); + v = detail::constant_expr<T>::saturate_cast(val.raw()); + } + else + { + auto v_cast = static_cast<fixed_point<T>>(val); + v_cast.rescale(p); + v = v_cast.raw(); + } + _value = static_cast<T>(v); + } + /** Constructor (from integer) + * + * @param[in] val Integer value to be represented as fixed point + * @param[in] p Fixed point precision + * @param[in] is_raw If true val is a raw fixed point value else an integer + */ + template <typename U, typename = typename std::enable_if<std::is_integral<U>::value>::type> + fixed_point(U val, uint8_t p, bool is_raw = false) + : _value(val << p), _fixed_point_position(p) + { + if(is_raw) + { + _value = val; + } + } + /** Constructor (from float) + * + * @param[in] val Float value to be represented as fixed point + * @param[in] p Fixed point precision + */ + fixed_point(float val, uint8_t p) + : _value(detail::constant_expr<T>::to_fixed(val, p)), _fixed_point_position(p) + { + assert(p > 0 && p < std::numeric_limits<T>::digits); + } + /** Constructor (from float string) + * + * @param[in] str Float string to be represented as fixed point + * @param[in] p Fixed point precision + */ + fixed_point(std::string str, uint8_t p) + : _value(detail::constant_expr<T>::to_fixed(arm_compute::test::cpp11::stof(str), p)), _fixed_point_position(p) + { + assert(p > 0 && p < std::numeric_limits<T>::digits); + } + /** Default copy constructor */ + fixed_point &operator=(const fixed_point &) = default; + /** Default move constructor */ + fixed_point &operator=(fixed_point &&) = default; + /** Default copy assignment operator */ + fixed_point(const fixed_point &) = default; + /** Default move assignment operator */ + fixed_point(fixed_point &&) = default; + + /** Float conversion operator + * + * @return Float representation of fixed point + */ + operator float() const + { + return detail::constant_expr<T>::to_float(_value, _fixed_point_position); + } + /** Integer conversion operator + * + * @return Integer representation of fixed point + */ + template <typename U, typename = typename std::enable_if<std::is_integral<T>::value>::type> + operator U() const + { + return detail::constant_expr<T>::to_int(_value, _fixed_point_position); + } + /** Convert to different fixed point of different type but same precision + * + * @note Down-conversion might fail. + */ + template <typename U> + operator fixed_point<U>() + { + U val = static_cast<U>(_value); + if(std::numeric_limits<U>::digits < std::numeric_limits<T>::digits) + { + val = detail::constant_expr<U>::saturate_cast(_value); + } + return fixed_point<U>(val, _fixed_point_position, true); + } + + /** Arithmetic += assignment operator + * + * @param[in] rhs Fixed point operand + * + * @return Reference to this fixed point + */ + template <typename U> + fixed_point<T> &operator+=(const fixed_point<U> &rhs) + { + fixed_point<T> val(rhs, _fixed_point_position); + _value += val.raw(); + return *this; + } + /** Arithmetic -= assignment operator + * + * @param[in] rhs Fixed point operand + * + * @return Reference to this fixed point + */ + template <typename U> + fixed_point<T> &operator-=(const fixed_point<U> &rhs) + { + fixed_point<T> val(rhs, _fixed_point_position); + _value -= val.raw(); + return *this; + } + + /** Raw value accessor + * + * @return Raw fixed point value + */ + T raw() const + { + return _value; + } + /** Precision accessor + * + * @return Precision of fixed point + */ + uint8_t precision() const + { + return _fixed_point_position; + } + /** Rescale a fixed point to a new precision + * + * @param[in] p New fixed point precision + */ + void rescale(uint8_t p) + { + assert(p > 0 && p < std::numeric_limits<T>::digits); + + if(p > _fixed_point_position) + { + _value <<= (p - _fixed_point_position); + } + else if(p < _fixed_point_position) + { + _value >>= (_fixed_point_position - p); + } + + _fixed_point_position = p; + } + +private: + T _value; /**< Fixed point raw value */ + uint8_t _fixed_point_position; /**< Fixed point precision */ +}; + +namespace detail +{ +/** Count the number of leading zero bits in the given value. + * + * @param[in] value Input value. + * + * @return Number of leading zero bits. + */ +template <typename T> +constexpr int clz(T value) +{ + using unsigned_T = typename std::make_unsigned<T>::type; + // __builtin_clz is available for int. Need to correct reported number to + // match the original type. + return __builtin_clz(value) - (32 - std::numeric_limits<unsigned_T>::digits); +} + +template <typename T> +struct constant_expr +{ + /** Calculate representation of 1 in fixed point given a fixed point precision + * + * @param[in] p Fixed point precision + * + * @return Representation of value 1 in fixed point. + */ + static constexpr T fixed_one(uint8_t p) + { + return (1 << p); + } + /** Calculate fixed point precision step given a fixed point precision + * + * @param[in] p Fixed point precision + * + * @return Fixed point precision step + */ + static constexpr float fixed_step(uint8_t p) + { + return (1.0f / static_cast<float>(1 << p)); + } + + /** Convert a fixed point value to float given its precision. + * + * @param[in] val Fixed point value + * @param[in] p Fixed point precision + * + * @return Float representation of the fixed point number + */ + static constexpr float to_float(T val, uint8_t p) + { + return static_cast<float>(val * fixed_step(p)); + } + /** Convert a fixed point value to integer given its precision. + * + * @param[in] val Fixed point value + * @param[in] p Fixed point precision + * + * @return Integer of the fixed point number + */ + static constexpr T to_int(T val, uint8_t p) + { + return val >> p; + } + /** Convert a single precision floating point value to a fixed point representation given its precision. + * + * @param[in] val Floating point value + * @param[in] p Fixed point precision + * + * @return The raw fixed point representation + */ + static constexpr T to_fixed(float val, uint8_t p) + { + return static_cast<T>(val * fixed_one(p) + ((val >= 0) ? 0.5 : -0.5)); + } + /** Clamp value between two ranges + * + * @param[in] val Value to clamp + * @param[in] min Minimum value to clamp to + * @param[in] max Maximum value to clamp to + * + * @return clamped value + */ + static constexpr T clamp(T val, T min, T max) + { + return std::min(std::max(val, min), max); + } + /** Saturate given number + * + * @param[in] val Value to saturate + * + * @return Saturated value + */ + template <typename U> + static constexpr T saturate_cast(U val) + { + return static_cast<T>(std::min<U>(std::max<U>(val, static_cast<U>(std::numeric_limits<T>::min())), static_cast<U>(std::numeric_limits<T>::max()))); + } +}; +struct functions +{ + /** Output stream operator + * + * @param[in] s Output stream + * @param[in] x Fixed point value + * + * @return Reference output to updated stream + */ + template <typename T, typename U, typename traits> + static std::basic_ostream<T, traits> &write(std::basic_ostream<T, traits> &s, fixed_point<U> &x) + { + return s << static_cast<float>(x); + } + /** Signbit of a fixed point number. + * + * @param[in] x Fixed point number + * + * @return True if negative else false. + */ + template <typename T> + static bool signbit(fixed_point<T> x) + { + return ((x._value >> std::numeric_limits<T>::digits) != 0); + } + /** Checks if two fixed point numbers are equal + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return True if fixed points are equal else false + */ + template <typename T> + static bool isequal(fixed_point<T> x, fixed_point<T> y) + { + uint8_t p = std::min(x._fixed_point_position, y._fixed_point_position); + x.rescale(p); + y.rescale(p); + return (x._value == y._value); + } + /** Checks if two fixed point number are not equal + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return True if fixed points are not equal else false + */ + template <typename T> + static bool isnotequal(fixed_point<T> x, fixed_point<T> y) + { + return !isequal(x, y); + } + /** Checks if one fixed point is greater than the other + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return True if fixed point is greater than other + */ + template <typename T> + static bool isgreater(fixed_point<T> x, fixed_point<T> y) + { + uint8_t p = std::min(x._fixed_point_position, y._fixed_point_position); + x.rescale(p); + y.rescale(p); + return (x._value > y._value); + } + /** Checks if one fixed point is greater or equal than the other + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return True if fixed point is greater or equal than other + */ + template <typename T> + static bool isgreaterequal(fixed_point<T> x, fixed_point<T> y) + { + uint8_t p = std::min(x._fixed_point_position, y._fixed_point_position); + x.rescale(p); + y.rescale(p); + return (x._value >= y._value); + } + /** Checks if one fixed point is less than the other + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return True if fixed point is less than other + */ + template <typename T> + static bool isless(fixed_point<T> x, fixed_point<T> y) + { + uint8_t p = std::min(x._fixed_point_position, y._fixed_point_position); + x.rescale(p); + y.rescale(p); + return (x._value < y._value); + } + /** Checks if one fixed point is less or equal than the other + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return True if fixed point is less or equal than other + */ + template <typename T> + static bool islessequal(fixed_point<T> x, fixed_point<T> y) + { + uint8_t p = std::min(x._fixed_point_position, y._fixed_point_position); + x.rescale(p); + y.rescale(p); + return (x._value <= y._value); + } + /** Checks if one fixed point is less or greater than the other + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return True if fixed point is less or greater than other + */ + template <typename T> + static bool islessgreater(fixed_point<T> x, fixed_point<T> y) + { + return isnotequal(x, y); + } + /** Clamp fixed point to specific range. + * + * @param[in] x Fixed point operand + * @param[in] min Minimum value to clamp to + * @param[in] max Maximum value to clamp to + * + * @return Clamped result + */ + template <typename T> + static fixed_point<T> clamp(fixed_point<T> x, T min, T max) + { + return fixed_point<T>(constant_expr<T>::clamp(x._value, min, max), x._fixed_point_position, true); + } + /** Negate number + * + * @param[in] x Fixed point operand + * + * @return Negated fixed point result + */ + template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> + static fixed_point<T> negate(fixed_point<T> x) + { + using promoted_T = typename traits::promote<T>::type; + promoted_T val = -x._value; + if(OP == OverflowPolicy::SATURATE) + { + val = constant_expr<T>::saturate_cast(val); + } + return fixed_point<T>(static_cast<T>(val), x._fixed_point_position, true); + } + /** Perform addition among two fixed point numbers + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return Result fixed point with precision equal to minimum precision of both operands + */ + template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> + static fixed_point<T> add(fixed_point<T> x, fixed_point<T> y) + { + uint8_t p = std::min(x._fixed_point_position, y._fixed_point_position); + x.rescale(p); + y.rescale(p); + if(OP == OverflowPolicy::SATURATE) + { + using type = typename traits::promote<T>::type; + type val = static_cast<type>(x._value) + static_cast<type>(y._value); + val = constant_expr<T>::saturate_cast(val); + return fixed_point<T>(static_cast<T>(val), p, true); + } + else + { + return fixed_point<T>(x._value + y._value, p, true); + } + } + /** Perform subtraction among two fixed point numbers + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return Result fixed point with precision equal to minimum precision of both operands + */ + template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> + static fixed_point<T> sub(fixed_point<T> x, fixed_point<T> y) + { + uint8_t p = std::min(x._fixed_point_position, y._fixed_point_position); + x.rescale(p); + y.rescale(p); + if(OP == OverflowPolicy::SATURATE) + { + using type = typename traits::promote<T>::type; + type val = static_cast<type>(x._value) - static_cast<type>(y._value); + val = constant_expr<T>::saturate_cast(val); + return fixed_point<T>(static_cast<T>(val), p, true); + } + else + { + return fixed_point<T>(x._value - y._value, p, true); + } + } + /** Perform multiplication among two fixed point numbers + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return Result fixed point with precision equal to minimum precision of both operands + */ + template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> + static fixed_point<T> mul(fixed_point<T> x, fixed_point<T> y) + { + using promoted_T = typename traits::promote<T>::type; + uint8_t p_min = std::min(x._fixed_point_position, y._fixed_point_position); + uint8_t p_max = std::max(x._fixed_point_position, y._fixed_point_position); + promoted_T round_factor = (1 << (p_max - 1)); + promoted_T val = ((static_cast<promoted_T>(x._value) * static_cast<promoted_T>(y._value)) + round_factor) >> p_max; + if(OP == OverflowPolicy::SATURATE) + { + val = constant_expr<T>::saturate_cast(val); + } + return fixed_point<T>(static_cast<T>(val), p_min, true); + } + /** Perform division among two fixed point numbers + * + * @param[in] x First fixed point operand + * @param[in] y Second fixed point operand + * + * @return Result fixed point with precision equal to minimum precision of both operands + */ + template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> + static fixed_point<T> div(fixed_point<T> x, fixed_point<T> y) + { + using promoted_T = typename traits::promote<T>::type; + uint8_t p = std::min(x._fixed_point_position, y._fixed_point_position); + promoted_T denom = static_cast<promoted_T>(y._value); + if(denom != 0) + { + promoted_T val = (static_cast<promoted_T>(x._value) << std::max(x._fixed_point_position, y._fixed_point_position)) / denom; + if(OP == OverflowPolicy::SATURATE) + { + val = constant_expr<T>::saturate_cast(val); + } + return fixed_point<T>(static_cast<T>(val), p, true); + } + else + { + T val = (x._value < 0) ? std::numeric_limits<T>::min() : std::numeric_limits<T>::max(); + return fixed_point<T>(val, p, true); + } + } + /** Shift left + * + * @param[in] x Fixed point operand + * @param[in] shift Shift value + * + * @return Shifted value + */ + template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> + static fixed_point<T> shift_left(fixed_point<T> x, size_t shift) + { + using promoted_T = typename traits::promote<T>::type; + promoted_T val = static_cast<promoted_T>(x._value) << shift; + if(OP == OverflowPolicy::SATURATE) + { + val = constant_expr<T>::saturate_cast(val); + } + return fixed_point<T>(static_cast<T>(val), x._fixed_point_position, true); + } + /** Shift right + * + * @param[in] x Fixed point operand + * @param[in] shift Shift value + * + * @return Shifted value + */ + template <typename T> + static fixed_point<T> shift_right(fixed_point<T> x, size_t shift) + { + return fixed_point<T>(x._value >> shift, x._fixed_point_position, true); + } + /** Calculate absolute value + * + * @param[in] x Fixed point operand + * + * @return Absolute value of operand + */ + template <typename T> + static fixed_point<T> abs(fixed_point<T> x) + { + using promoted_T = typename traits::promote<T>::type; + T val = (x._value < 0) ? constant_expr<T>::saturate_cast(-static_cast<promoted_T>(x._value)) : x._value; + return fixed_point<T>(val, x._fixed_point_position, true); + } + /** Calculate the logarithm of a fixed point number + * + * @param[in] x Fixed point operand + * + * @return Logarithm value of operand + */ + template <typename T> + static fixed_point<T> log(fixed_point<T> x) + { + uint8_t p = x._fixed_point_position; + auto const_one = fixed_point<T>(static_cast<T>(1), p); + + // Logarithm of 1 is zero and logarithm of negative values is not defined in R, so return 0. + // Also, log(x) == -log(1/x) for 0 < x < 1. + if(isequal(x, const_one) || islessequal(x, fixed_point<T>(static_cast<T>(0), p))) + { + return fixed_point<T>(static_cast<T>(0), p, true); + } + else if(isless(x, const_one)) + { + return mul(log(div(const_one, x)), fixed_point<T>(-1, p)); + } + + // Remove even powers of 2 + T shift_val = 31 - __builtin_clz(x._value >> p); + x = shift_right(x, shift_val); + x = sub(x, const_one); + + // Constants + auto ln2 = fixed_point<T>(0.6931471, p); + auto A = fixed_point<T>(1.4384189, p); + auto B = fixed_point<T>(-0.67719, p); + auto C = fixed_point<T>(0.3218538, p); + auto D = fixed_point<T>(-0.0832229, p); + + // Polynomial expansion + auto sum = add(mul(x, D), C); + sum = add(mul(x, sum), B); + sum = add(mul(x, sum), A); + sum = mul(x, sum); + + return mul(add(sum, fixed_point<T>(static_cast<T>(shift_val), p)), ln2); + } + /** Calculate the exponential of a fixed point number. + * + * exp(x) = exp(floor(x)) * exp(x - floor(x)) + * = pow(2, floor(x) / ln(2)) * exp(x - floor(x)) + * = exp(x - floor(x)) << (floor(x) / ln(2)) + * + * @param[in] x Fixed point operand + * + * @return Exponential value of operand + */ + template <typename T> + static fixed_point<T> exp(fixed_point<T> x) + { + uint8_t p = x._fixed_point_position; + // Constants + auto const_one = fixed_point<T>(1, p); + auto ln2 = fixed_point<T>(0.6931471, p); + auto inv_ln2 = fixed_point<T>(1.442695, p); + auto A = fixed_point<T>(0.9978546, p); + auto B = fixed_point<T>(0.4994721, p); + auto C = fixed_point<T>(0.1763723, p); + auto D = fixed_point<T>(0.0435108, p); + + T scaled_int_part = detail::constant_expr<T>::to_int(mul(x, inv_ln2)._value, p); + + // Polynomial expansion + auto frac_part = sub(x, mul(ln2, fixed_point<T>(scaled_int_part, p))); + auto taylor = add(mul(frac_part, D), C); + taylor = add(mul(frac_part, taylor), B); + taylor = add(mul(frac_part, taylor), A); + taylor = mul(frac_part, taylor); + taylor = add(taylor, const_one); + + // Saturate value + if(static_cast<T>(clz(taylor.raw())) <= scaled_int_part) + { + return fixed_point<T>(std::numeric_limits<T>::max(), p, true); + } + + return (scaled_int_part < 0) ? shift_right(taylor, -scaled_int_part) : shift_left(taylor, scaled_int_part); + } + /** Calculate the inverse square root of a fixed point number + * + * @param[in] x Fixed point operand + * + * @return Inverse square root value of operand + */ + template <typename T> + static fixed_point<T> inv_sqrt(fixed_point<T> x) + { + const uint8_t p = x._fixed_point_position; + int8_t shift = std::numeric_limits<T>::digits - (p + detail::clz(x._value)); + + shift += std::numeric_limits<T>::is_signed ? 1 : 0; + + const auto three_half = fixed_point<T>(1.5f, p); + fixed_point<T> a = shift < 0 ? shift_left(x, -shift) : shift_right(x, shift); + const fixed_point<T> x_half = shift_right(a, 1); + + // We need three iterations to find the result + for(int i = 0; i < 3; ++i) + { + a = mul(a, sub(three_half, mul(x_half, mul(a, a)))); + } + + return (shift < 0) ? shift_left(a, -shift >> 1) : shift_right(a, shift >> 1); + } + /** Calculate the hyperbolic tangent of a fixed point number + * + * @param[in] x Fixed point operand + * + * @return Hyperbolic tangent of the operand + */ + template <typename T> + static fixed_point<T> tanh(fixed_point<T> x) + { + uint8_t p = x._fixed_point_position; + // Constants + auto const_one = fixed_point<T>(1, p); + auto const_two = fixed_point<T>(2, p); + + auto exp2x = exp(const_two * x); + auto num = exp2x - const_one; + auto den = exp2x + const_one; + auto tanh = num / den; + + return tanh; + } + /** Calculate the a-th power of a fixed point number. + * + * The power is computed as x^a = e^(log(x) * a) + * + * @param[in] x Fixed point operand + * @param[in] a Fixed point exponent + * + * @return a-th power of the operand + */ + template <typename T> + static fixed_point<T> pow(fixed_point<T> x, fixed_point<T> a) + { + return exp(log(x) * a); + } +}; + +template <typename T> +bool operator==(const fixed_point<T> &lhs, const fixed_point<T> &rhs) +{ + return functions::isequal(lhs, rhs); +} +template <typename T> +bool operator!=(const fixed_point<T> &lhs, const fixed_point<T> &rhs) +{ + return !operator==(lhs, rhs); +} +template <typename T> +bool operator<(const fixed_point<T> &lhs, const fixed_point<T> &rhs) +{ + return functions::isless(lhs, rhs); +} +template <typename T> +bool operator>(const fixed_point<T> &lhs, const fixed_point<T> &rhs) +{ + return operator<(rhs, lhs); +} +template <typename T> +bool operator<=(const fixed_point<T> &lhs, const fixed_point<T> &rhs) +{ + return !operator>(lhs, rhs); +} +template <typename T> +bool operator>=(const fixed_point<T> &lhs, const fixed_point<T> &rhs) +{ + return !operator<(lhs, rhs); +} +template <typename T> +fixed_point<T> operator+(const fixed_point<T> &lhs, const fixed_point<T> &rhs) +{ + return functions::add(lhs, rhs); +} +template <typename T> +fixed_point<T> operator-(const fixed_point<T> &lhs, const fixed_point<T> &rhs) +{ + return functions::sub(lhs, rhs); +} +template <typename T> +fixed_point<T> operator-(const fixed_point<T> &rhs) +{ + return functions::negate(rhs); +} +template <typename T> +fixed_point<T> operator*(fixed_point<T> x, fixed_point<T> y) +{ + return functions::mul(x, y); +} +template <typename T> +fixed_point<T> operator/(fixed_point<T> x, fixed_point<T> y) +{ + return functions::div(x, y); +} +template <typename T> +fixed_point<T> operator>>(fixed_point<T> x, size_t shift) +{ + return functions::shift_right(x, shift); +} +template <typename T> +fixed_point<T> operator<<(fixed_point<T> x, size_t shift) +{ + return functions::shift_left(x, shift); +} +template <typename T, typename U, typename traits> +std::basic_ostream<T, traits> &operator<<(std::basic_ostream<T, traits> &s, fixed_point<U> x) +{ + return functions::write(s, x); +} +template <typename T> +inline fixed_point<T> min(fixed_point<T> x, fixed_point<T> y) +{ + return x > y ? y : x; +} +template <typename T> +inline fixed_point<T> max(fixed_point<T> x, fixed_point<T> y) +{ + return x > y ? x : y; +} +template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> +inline fixed_point<T> add(fixed_point<T> x, fixed_point<T> y) +{ + return functions::add<OP>(x, y); +} +template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> +inline fixed_point<T> sub(fixed_point<T> x, fixed_point<T> y) +{ + return functions::sub<OP>(x, y); +} +template <OverflowPolicy OP = OverflowPolicy::SATURATE, typename T> +inline fixed_point<T> mul(fixed_point<T> x, fixed_point<T> y) +{ + return functions::mul<OP>(x, y); +} +template <typename T> +inline fixed_point<T> div(fixed_point<T> x, fixed_point<T> y) +{ + return functions::div(x, y); +} +template <typename T> +inline fixed_point<T> abs(fixed_point<T> x) +{ + return functions::abs(x); +} +template <typename T> +inline fixed_point<T> clamp(fixed_point<T> x, T min, T max) +{ + return functions::clamp(x, min, max); +} +template <typename T> +inline fixed_point<T> exp(fixed_point<T> x) +{ + return functions::exp(x); +} +template <typename T> +inline fixed_point<T> log(fixed_point<T> x) +{ + return functions::log(x); +} +template <typename T> +inline fixed_point<T> inv_sqrt(fixed_point<T> x) +{ + return functions::inv_sqrt(x); +} +template <typename T> +inline fixed_point<T> tanh(fixed_point<T> x) +{ + return functions::tanh(x); +} +template <typename T> +inline fixed_point<T> pow(fixed_point<T> x, fixed_point<T> a) +{ + return functions::pow(x, a); +} +} // namespace detail + +// Expose operators +using detail::operator==; +using detail::operator!=; +using detail::operator<; +using detail::operator>; +using detail::operator<=; +using detail::operator>=; +using detail::operator+; +using detail::operator-; +using detail::operator*; +using detail::operator/; +using detail::operator>>; +using detail::operator<<; + +// Expose additional functions +using detail::min; +using detail::max; +using detail::add; +using detail::sub; +using detail::mul; +using detail::div; +using detail::abs; +using detail::clamp; +using detail::exp; +using detail::log; +using detail::inv_sqrt; +using detail::tanh; +using detail::pow; +// TODO: floor +// TODO: ceil +// TODO: sqrt +} // namespace fixed_point_arithmetic +} // namespace test +} // namespace arm_compute +#endif /*__ARM_COMPUTE_TEST_VALIDATION_FIXEDPOINT_H__ */ diff --git a/tests/validation/Helpers.h b/tests/validation/Helpers.h new file mode 100644 index 0000000000..cbaea4b894 --- /dev/null +++ b/tests/validation/Helpers.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_VALIDATION_HELPERS_H__ +#define __ARM_COMPUTE_TEST_VALIDATION_HELPERS_H__ + +#include "Types.h" + +#include <type_traits> +#include <utility> + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +/** Helper function to get the testing range for each activation layer. + * + * @param[in] activation Activation function to test. + * @param[in] fixed_point_position (Optional) Number of bits for the fractional part. Defaults to 1. + * + * @return A pair containing the lower upper testing bounds for a given function. + */ +template <typename T> +std::pair<T, T> get_activation_layer_test_bounds(ActivationLayerInfo::ActivationFunction activation, int fixed_point_position = 1) +{ + bool is_float = std::is_floating_point<T>::value; + std::pair<T, T> bounds; + + // Set initial values + if(is_float) + { + bounds = std::make_pair(-255.f, 255.f); + } + else + { + bounds = std::make_pair(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::max()); + } + + // Reduce testing ranges + switch(activation) + { + case ActivationLayerInfo::ActivationFunction::LOGISTIC: + case ActivationLayerInfo::ActivationFunction::SOFT_RELU: + // Reduce range as exponent overflows + if(is_float) + { + bounds.first = -40.f; + bounds.second = 40.f; + } + else + { + bounds.first = -(1 << (fixed_point_position)); + bounds.second = 1 << (fixed_point_position); + } + break; + case ActivationLayerInfo::ActivationFunction::TANH: + // Reduce range as exponent overflows + if(!is_float) + { + bounds.first = -(1 << (fixed_point_position)); + bounds.second = 1 << (fixed_point_position); + } + break; + case ActivationLayerInfo::ActivationFunction::SQRT: + // Reduce range as sqrt should take a non-negative number + bounds.first = (is_float) ? 0 : 1 << (fixed_point_position); + break; + default: + break; + } + return bounds; +} + +/** Helper function to get the testing range for batch normalization layer. + * + * @param[in] fixed_point_position (Optional) Number of bits for the fractional part. Defaults to 1. + * + * @return A pair containing the lower upper testing bounds. + */ +template <typename T> +std::pair<T, T> get_batchnormalization_layer_test_bounds(int fixed_point_position = 1) +{ + bool is_float = std::is_floating_point<T>::value; + std::pair<T, T> bounds; + + // Set initial values + if(is_float) + { + bounds = std::make_pair(-1.f, 1.f); + } + else + { + bounds = std::make_pair(1, 1 << (fixed_point_position)); + } + + return bounds; +} +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif //__ARM_COMPUTE_TEST_VALIDATION_HELPERS_H__ diff --git a/tests/validation/NEON/AbsoluteDifference.cpp b/tests/validation/NEON/AbsoluteDifference.cpp new file mode 100644 index 0000000000..b7f45d2384 --- /dev/null +++ b/tests/validation/NEON/AbsoluteDifference.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEAbsoluteDifference.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon absolute difference function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * + * @return Computed output tensor. + */ +Tensor compute_absolute_difference(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt_in0); + Tensor src2 = create_tensor(shape, dt_in1); + Tensor dst = create_tensor(shape, dt_out); + + // Create and configure function + NEAbsoluteDifference abs_d; + abs_d.configure(&src1, &src2, &dst); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src1), 0); + library->fill_tensor_uniform(NEAccessor(src2), 1); + + // Compute function + abs_d.run(); + + return dst; +} + +void validate_configuration(const Tensor &src1, const Tensor &src2, Tensor &dst, TensorShape shape) +{ + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEAbsoluteDifference abs_d; + abs_d.configure(&src1, &src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(src2.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(AbsoluteDifference) + +BOOST_AUTO_TEST_SUITE(U8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()), + shape) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + validate_configuration(src1, src2, dst, shape); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), + shape) +{ + // Compute function + Tensor dst = compute_absolute_difference(shape, DataType::U8, DataType::U8, DataType::U8); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_absolute_difference(shape, DataType::U8, DataType::U8, DataType::U8); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), + shape) +{ + // Compute function + Tensor dst = compute_absolute_difference(shape, DataType::U8, DataType::U8, DataType::U8); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_absolute_difference(shape, DataType::U8, DataType::U8, DataType::U8); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(S16) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ DataType::U8, DataType::S16 }), + shape, dt) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt); + Tensor src2 = create_tensor(shape, DataType::S16); + Tensor dst = create_tensor(shape, DataType::S16); + + validate_configuration(src1, src2, dst, shape); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }), + shape, dt) +{ + // Compute function + Tensor dst = compute_absolute_difference(shape, dt, DataType::S16, DataType::S16); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_absolute_difference(shape, dt, DataType::S16, DataType::S16); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }), + shape, dt) +{ + // Compute function + Tensor dst = compute_absolute_difference(shape, dt, DataType::S16, DataType::S16); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_absolute_difference(shape, dt, DataType::S16, DataType::S16); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Accumulate.cpp b/tests/validation/NEON/Accumulate.cpp new file mode 100644 index 0000000000..e3ea37cd99 --- /dev/null +++ b/tests/validation/NEON/Accumulate.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEAccumulate.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon accumulate function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_accumulate(const TensorShape &shape) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::S16); + + // Create and configure function + NEAccumulate acc; + acc.configure(&src, &dst); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src), 0); + library->fill_tensor_uniform(NEAccessor(dst), 1); + + // Compute function + acc.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(Accumulate) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()), + shape) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::S16); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEAccumulate acc; + acc.configure(&src, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), + shape) +{ + // Compute function + Tensor dst = compute_accumulate(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_accumulate(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), + shape) +{ + // Compute function + Tensor dst = compute_accumulate(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_accumulate(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/AccumulateSquared.cpp b/tests/validation/NEON/AccumulateSquared.cpp new file mode 100644 index 0000000000..10263a02e3 --- /dev/null +++ b/tests/validation/NEON/AccumulateSquared.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEAccumulate.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon accumulate squared function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_accumulate_squared(const TensorShape &shape, uint32_t shift) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::S16); + + // Create and configure function + NEAccumulateSquared acc; + acc.configure(&src, shift, &dst); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + // dst tensor filled with non-negative values + library->fill_tensor_uniform(NEAccessor(src), 0); + library->fill_tensor_uniform(NEAccessor(dst), 1, static_cast<int16_t>(0), std::numeric_limits<int16_t>::max()); + + // Compute function + acc.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(AccumulateSquared) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::xrange(0U, 16U), + shape, shift) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::S16); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEAccumulateSquared acc; + acc.configure(&src, shift, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::xrange(0U, 16U), + shape, shift) +{ + // Compute function + Tensor dst = compute_accumulate_squared(shape, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_accumulate_squared(shape, shift); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ 0U, 1U, 15U }), + shape, shift) +{ + // Compute function + Tensor dst = compute_accumulate_squared(shape, shift); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_accumulate_squared(shape, shift); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/AccumulateWeighted.cpp b/tests/validation/NEON/AccumulateWeighted.cpp new file mode 100644 index 0000000000..6d45848647 --- /dev/null +++ b/tests/validation/NEON/AccumulateWeighted.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEAccumulate.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon accumulate weighted function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_accumulate_weighted(const TensorShape &shape, float alpha) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + // Create and configure function + NEAccumulateWeighted acc; + acc.configure(&src, alpha, &dst); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src), 0); + library->fill_tensor_uniform(NEAccessor(dst), 1); + + // Compute function + acc.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(AccumulateWeighted) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ 0.f, 0.5f, 1.f }), + shape, alpha) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEAccumulateWeighted acc; + acc.configure(&src, alpha, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ 0.f, 0.5f, 1.f }), + shape, alpha) +{ + // Compute function + Tensor dst = compute_accumulate_weighted(shape, alpha); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_accumulate_weighted(shape, alpha); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ 0.f, 0.5f, 1.f }), + shape, alpha) +{ + // Compute function + Tensor dst = compute_accumulate_weighted(shape, alpha); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_accumulate_weighted(shape, alpha); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/ActivationLayer.cpp b/tests/validation/NEON/ActivationLayer.cpp new file mode 100644 index 0000000000..da304d8087 --- /dev/null +++ b/tests/validation/NEON/ActivationLayer.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Helpers.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEActivationLayer.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> +#include <tuple> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Define tolerance of the activation layer + * + * @param[in] activation The activation function used. + * @param[in] fixed_point_position Number of bits for the fractional part.. + * + * @return Tolerance depending on the activation function. + */ +float activation_layer_tolerance(ActivationLayerInfo::ActivationFunction activation, int fixed_point_position = 0) +{ + switch(activation) + { + case ActivationLayerInfo::ActivationFunction::LOGISTIC: + case ActivationLayerInfo::ActivationFunction::SOFT_RELU: + case ActivationLayerInfo::ActivationFunction::SQRT: + case ActivationLayerInfo::ActivationFunction::TANH: + return (fixed_point_position != 0) ? 5.f : 0.00001f; + break; + default: + return 0.f; + } +} + +/** Compute Neon activation layer function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt Shape Data type of tensors. + * @param[in] act_info Activation layer information. + * @param[in] fixed_point_position Number of bits for the fractional part of fixed point numbers. + * + * @return Computed output tensor. + */ +Tensor compute_activation_layer(const TensorShape &shape, DataType dt, ActivationLayerInfo act_info, int fixed_point_position = 0) +{ + // Create tensors + Tensor src = create_tensor(shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(shape, dt, 1, fixed_point_position); + + // Create and configure function + NEActivationLayer act_layer; + act_layer.configure(&src, &dst, act_info); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + if(dt == DataType::F32) + { + float min_bound = 0; + float max_bound = 0; + std::tie(min_bound, max_bound) = get_activation_layer_test_bounds<float>(act_info.activation()); + std::uniform_real_distribution<> distribution(min_bound, max_bound); + library->fill(NEAccessor(src), distribution, 0); + } + else + { + int min_bound = 0; + int max_bound = 0; + std::tie(min_bound, max_bound) = get_activation_layer_test_bounds<int8_t>(act_info.activation(), fixed_point_position); + std::uniform_int_distribution<> distribution(min_bound, max_bound); + library->fill(NEAccessor(src), distribution, 0); + } + + // Compute function + act_layer.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(ActivationLayer) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * CNNDataTypes(), shape, dt) +{ + // Set fixed point position data type allowed + int fixed_point_position = (arm_compute::is_data_type_fixed_point(dt)) ? 3 : 0; + + // Create tensors + Tensor src = create_tensor(shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(shape, dt, 1, fixed_point_position); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEActivationLayer act_layer; + act_layer.configure(&src, &dst, ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::ABS)); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * CNNFloatDataTypes() * ActivationFunctions(), shape, dt, act_function) +{ + // Create activation layer info + ActivationLayerInfo act_info(act_function, 1.f, 1.f); + + // Compute function + Tensor dst = compute_activation_layer(shape, dt, act_info); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_activation_layer(shape, dt, act_info); + + // Validate output + validate(NEAccessor(dst), ref_dst, activation_layer_tolerance(act_function)); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * CNNFloatDataTypes() * ActivationFunctions(), shape, dt, act_function) +{ + // Create activation layer info + ActivationLayerInfo act_info(act_function, 1.f, 1.f); + + // Compute function + Tensor dst = compute_activation_layer(shape, dt, act_info); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_activation_layer(shape, dt, act_info); + + // Validate output + validate(NEAccessor(dst), ref_dst, activation_layer_tolerance(act_function)); +} +BOOST_AUTO_TEST_SUITE_END() + +/** @note We test for fixed point precision [3,5] because [1,2] and [6,7] ranges + * cause overflowing issues in most of the transcendentals functions. + */ +BOOST_AUTO_TEST_SUITE(Quantized) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * ActivationFunctions() * boost::unit_test::data::xrange(3, 6, 1), + shape, act_function, fixed_point_position) +{ + // Create activation layer info + ActivationLayerInfo act_info(act_function, 1.f, 1.f); + + // Compute function + Tensor dst = compute_activation_layer(shape, DataType::QS8, act_info, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_activation_layer(shape, DataType::QS8, act_info, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, activation_layer_tolerance(act_function, fixed_point_position)); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/ArithmeticAddition.cpp b/tests/validation/NEON/ArithmeticAddition.cpp new file mode 100644 index 0000000000..5654a426fd --- /dev/null +++ b/tests/validation/NEON/ArithmeticAddition.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEArithmeticAddition.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon arithmetic addition function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] policy Overflow policy of the operation. + * + * @return Computed output tensor. + */ +Tensor compute_arithmetic_addition(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, ConvertPolicy policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt_in0); + Tensor src2 = create_tensor(shape, dt_in1); + Tensor dst = create_tensor(shape, dt_out); + + // Create and configure function + NEArithmeticAddition add; + add.configure(&src1, &src2, &dst, policy); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src1), 0); + library->fill_tensor_uniform(NEAccessor(src2), 1); + + // Compute function + add.run(); + + return dst; +} + +void validate_configuration(const Tensor &src1, const Tensor &src2, Tensor &dst, TensorShape shape, ConvertPolicy policy) +{ + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEArithmeticAddition add; + add.configure(&src1, &src2, &dst, policy); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(src2.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(ArithmeticAddition) + +BOOST_AUTO_TEST_SUITE(U8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + validate_configuration(src1, src2, dst, shape, policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, policy) +{ + // Compute function + Tensor dst = compute_arithmetic_addition(shape, DataType::U8, DataType::U8, DataType::U8, policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_addition(shape, DataType::U8, DataType::U8, DataType::U8, policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(S16) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, dt, policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt); + Tensor src2 = create_tensor(shape, DataType::S16); + Tensor dst = create_tensor(shape, DataType::S16); + + validate_configuration(src1, src2, dst, shape, policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, dt, policy) +{ + // Compute function + Tensor dst = compute_arithmetic_addition(shape, dt, DataType::S16, DataType::S16, policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_addition(shape, dt, DataType::S16, DataType::S16, policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, dt, policy) +{ + // Compute function + Tensor dst = compute_arithmetic_addition(shape, dt, DataType::S16, DataType::S16, policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_addition(shape, dt, DataType::S16, DataType::S16, policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(F32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::F32); + Tensor src2 = create_tensor(shape, DataType::F32); + Tensor dst = create_tensor(shape, DataType::F32); + + validate_configuration(src1, src2, dst, shape, policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + Tensor dst = compute_arithmetic_addition(shape, DataType::F32, DataType::F32, DataType::F32, ConvertPolicy::WRAP); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_addition(shape, DataType::F32, DataType::F32, DataType::F32, ConvertPolicy::WRAP); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, policy) +{ + // Compute function + Tensor dst = compute_arithmetic_addition(shape, DataType::F32, DataType::F32, DataType::F32, policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_addition(shape, DataType::F32, DataType::F32, DataType::F32, policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/ArithmeticSubtraction.cpp b/tests/validation/NEON/ArithmeticSubtraction.cpp new file mode 100644 index 0000000000..9c0e9131e0 --- /dev/null +++ b/tests/validation/NEON/ArithmeticSubtraction.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEArithmeticSubtraction.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon arithmetic subtraction function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] policy Overflow policy of the operation. + * + * @return Computed output tensor. + */ +Tensor compute_arithmetic_subtraction(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, ConvertPolicy policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt_in0); + Tensor src2 = create_tensor(shape, dt_in1); + Tensor dst = create_tensor(shape, dt_out); + + // Create and configure function + NEArithmeticSubtraction sub; + sub.configure(&src1, &src2, &dst, policy); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src1), 0); + library->fill_tensor_uniform(NEAccessor(src2), 1); + + // Compute function + sub.run(); + + return dst; +} + +void validate_configuration(const Tensor &src1, const Tensor &src2, Tensor &dst, TensorShape shape, ConvertPolicy policy) +{ + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEArithmeticSubtraction sub; + sub.configure(&src1, &src2, &dst, policy); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(src2.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(ArithmeticSubtraction) + +BOOST_AUTO_TEST_SUITE(U8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + validate_configuration(src1, src2, dst, shape, policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, policy) +{ + // Compute function + Tensor dst = compute_arithmetic_subtraction(shape, DataType::U8, DataType::U8, DataType::U8, policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_subtraction(shape, DataType::U8, DataType::U8, DataType::U8, policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(S16) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, dt, policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt); + Tensor src2 = create_tensor(shape, DataType::S16); + Tensor dst = create_tensor(shape, DataType::S16); + + validate_configuration(src1, src2, dst, shape, policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, dt, policy) +{ + // Compute function + Tensor dst = compute_arithmetic_subtraction(shape, dt, DataType::S16, DataType::S16, policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_subtraction(shape, dt, DataType::S16, DataType::S16, policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, dt, policy) +{ + // Compute function + Tensor dst = compute_arithmetic_subtraction(shape, dt, DataType::S16, DataType::S16, policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_subtraction(shape, dt, DataType::S16, DataType::S16, policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(F32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::F32); + Tensor src2 = create_tensor(shape, DataType::F32); + Tensor dst = create_tensor(shape, DataType::F32); + + validate_configuration(src1, src2, dst, shape, policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + Tensor dst = compute_arithmetic_subtraction(shape, DataType::F32, DataType::F32, DataType::F32, ConvertPolicy::WRAP); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_subtraction(shape, DataType::F32, DataType::F32, DataType::F32, ConvertPolicy::WRAP); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }), + shape, policy) +{ + // Compute function + Tensor dst = compute_arithmetic_subtraction(shape, DataType::F32, DataType::F32, DataType::F32, policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_arithmetic_subtraction(shape, DataType::F32, DataType::F32, DataType::F32, policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/BatchNormalizationLayer.cpp b/tests/validation/NEON/BatchNormalizationLayer.cpp new file mode 100644 index 0000000000..7656b2f392 --- /dev/null +++ b/tests/validation/NEON/BatchNormalizationLayer.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TypePrinter.h" +#include "dataset/BatchNormalizationLayerDataset.h" +#include "tests/validation/Helpers.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/runtime/NEON/functions/NEBatchNormalizationLayer.h" + +#include <random> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance_f = 1e-05; /**< Tolerance value for comparing reference's output against floating point implementation's output */ +const float tolerance_q = 3; /**< Tolerance value for comparing reference's output against quantized implementation's output */ + +/** Compute Neon batch normalization function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt Data type of input and output tensors. + * @param[in] norm_info Normalization Layer information. + * + * @return Computed output tensor. + */ +Tensor compute_reference_batch_normalization_layer(const TensorShape &shape0, const TensorShape &shape1, DataType dt, float epsilon, int fixed_point_position = 0) +{ + // Create tensors + Tensor src = create_tensor(shape0, dt, 1, fixed_point_position); + Tensor dst = create_tensor(shape0, dt, 1, fixed_point_position); + Tensor mean = create_tensor(shape1, dt, 1, fixed_point_position); + Tensor var = create_tensor(shape1, dt, 1, fixed_point_position); + Tensor beta = create_tensor(shape1, dt, 1, fixed_point_position); + Tensor gamma = create_tensor(shape1, dt, 1, fixed_point_position); + + // Create and configure function + NEBatchNormalizationLayer norm; + norm.configure(&src, &dst, &mean, &var, &beta, &gamma, epsilon); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + mean.allocator()->allocate(); + var.allocator()->allocate(); + beta.allocator()->allocate(); + gamma.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + BOOST_TEST(!mean.info()->is_resizable()); + BOOST_TEST(!var.info()->is_resizable()); + BOOST_TEST(!beta.info()->is_resizable()); + BOOST_TEST(!gamma.info()->is_resizable()); + + // Fill tensors + if(dt == DataType::F32) + { + float min_bound = 0.f; + float max_bound = 0.f; + std::tie(min_bound, max_bound) = get_batchnormalization_layer_test_bounds<float>(); + std::uniform_real_distribution<> distribution(min_bound, max_bound); + std::uniform_real_distribution<> distribution_var(0, max_bound); + library->fill(NEAccessor(src), distribution, 0); + library->fill(NEAccessor(mean), distribution, 1); + library->fill(NEAccessor(var), distribution_var, 0); + library->fill(NEAccessor(beta), distribution, 3); + library->fill(NEAccessor(gamma), distribution, 4); + } + else + { + int min_bound = 0; + int max_bound = 0; + std::tie(min_bound, max_bound) = get_batchnormalization_layer_test_bounds<int8_t>(fixed_point_position); + std::uniform_int_distribution<> distribution(min_bound, max_bound); + std::uniform_int_distribution<> distribution_var(0, max_bound); + library->fill(NEAccessor(src), distribution, 0); + library->fill(NEAccessor(mean), distribution, 1); + library->fill(NEAccessor(var), distribution_var, 0); + library->fill(NEAccessor(beta), distribution, 3); + library->fill(NEAccessor(gamma), distribution, 4); + } + + // Compute function + norm.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(BatchNormalizationLayer) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, RandomBatchNormalizationLayerDataset() * (boost::unit_test::data::make(DataType::F32) + boost::unit_test::data::make(DataType::QS8)), obj, dt) +{ + // Set fixed point position data type allowed + int fixed_point_position = (arm_compute::is_data_type_fixed_point(dt)) ? 3 : 0; + + // Create tensors + Tensor src = create_tensor(obj.shape0, dt, 1, fixed_point_position); + Tensor dst = create_tensor(obj.shape0, dt, 1, fixed_point_position); + Tensor mean = create_tensor(obj.shape1, dt, 1, fixed_point_position); + Tensor var = create_tensor(obj.shape1, dt, 1, fixed_point_position); + Tensor beta = create_tensor(obj.shape1, dt, 1, fixed_point_position); + Tensor gamma = create_tensor(obj.shape1, dt, 1, fixed_point_position); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + BOOST_TEST(mean.info()->is_resizable()); + BOOST_TEST(var.info()->is_resizable()); + BOOST_TEST(beta.info()->is_resizable()); + BOOST_TEST(gamma.info()->is_resizable()); + + // Create and configure function + NEBatchNormalizationLayer norm; + norm.configure(&src, &dst, &mean, &var, &beta, &gamma, obj.epsilon); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(obj.shape0); + const ValidRegion valid_region_vec = shape_to_valid_region(obj.shape1); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + validate(mean.info()->valid_region(), valid_region_vec); + validate(var.info()->valid_region(), valid_region_vec); + validate(beta.info()->valid_region(), valid_region_vec); + validate(gamma.info()->valid_region(), valid_region_vec); +} + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(Random, + RandomBatchNormalizationLayerDataset() * boost::unit_test::data::make(DataType::F32), + obj, dt) +{ + // Compute function + Tensor dst = compute_reference_batch_normalization_layer(obj.shape0, obj.shape1, dt, obj.epsilon); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_batch_normalization_layer(obj.shape0, obj.shape1, dt, obj.epsilon); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_f, 0); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(Quantized) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(Random, + RandomBatchNormalizationLayerDataset() * boost::unit_test::data::make(DataType::QS8) * boost::unit_test::data::xrange(1, 6), + obj, dt, fixed_point_position) +{ + // Compute function + Tensor dst = compute_reference_batch_normalization_layer(obj.shape0, obj.shape1, dt, obj.epsilon, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_batch_normalization_layer(obj.shape0, obj.shape1, dt, obj.epsilon, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_q, 0); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/BitwiseAnd.cpp b/tests/validation/NEON/BitwiseAnd.cpp new file mode 100644 index 0000000000..8c0eda992f --- /dev/null +++ b/tests/validation/NEON/BitwiseAnd.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEBitwiseAnd.h" +#include "arm_compute/runtime/SubTensor.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon bitwise and function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_bitwise_and(const TensorShape &shape) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + // Create and configure function + NEBitwiseAnd band; + band.configure(&src1, &src2, &dst); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src1), 0); + library->fill_tensor_uniform(NEAccessor(src2), 1); + + // Compute function + band.run(); + + return dst; +} + +/** Compute Neon bitwise and function that splits the input and output in two subtensor. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_bitwise_and_subtensor(const TensorShape &shape) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + // Create SubTensors + int coord_z = shape.z() / 2; + TensorShape sub_shape = shape; + sub_shape.set(2, coord_z); + + SubTensor src1_sub1(&src1, sub_shape, Coordinates()); + SubTensor src1_sub2(&src1, sub_shape, Coordinates(0, 0, coord_z)); + SubTensor src2_sub1(&src2, sub_shape, Coordinates()); + SubTensor src2_sub2(&src2, sub_shape, Coordinates(0, 0, coord_z)); + SubTensor dst_sub1(&dst, sub_shape, Coordinates()); + SubTensor dst_sub2(&dst, sub_shape, Coordinates(0, 0, coord_z)); + + // Create and configure function + NEBitwiseAnd band1, band2; + band1.configure(&src1_sub1, &src2_sub1, &dst_sub1); + band2.configure(&src1_sub2, &src2_sub2, &dst_sub2); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + std::uniform_int_distribution<> distribution(0, 255); + library->fill(NEAccessor(src1), distribution, 0); + library->fill(NEAccessor(src2), distribution, 1); + + // Compute function + band1.run(); + band2.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(BitwiseAnd) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, SmallShapes() + LargeShapes(), shape) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEBitwiseAnd band; + band.configure(&src1, &src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(src2.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + Tensor dst = compute_bitwise_and(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_and(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_AUTO_TEST_CASE(RunSubTensor) +{ + // Create shape + TensorShape shape(27U, 35U, 8U, 2U); + + // Compute function + Tensor dst = compute_bitwise_and_subtensor(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_and(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), shape) +{ + // Compute function + Tensor dst = compute_bitwise_and(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_and(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/BitwiseNot.cpp b/tests/validation/NEON/BitwiseNot.cpp new file mode 100644 index 0000000000..cb0a1fd0b5 --- /dev/null +++ b/tests/validation/NEON/BitwiseNot.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEBitwiseNot.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon bitwise not function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_bitwise_not(const TensorShape &shape) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + // Create not configure function + NEBitwiseNot bnot; + bnot.configure(&src, &dst); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src), 0); + + // Compute function + bnot.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(BitwiseNot) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, SmallShapes() + LargeShapes(), shape) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create not configure function + NEBitwiseNot bnot; + bnot.configure(&src, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + Tensor dst = compute_bitwise_not(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_not(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), shape) +{ + // Compute function + Tensor dst = compute_bitwise_not(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_not(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/BitwiseOr.cpp b/tests/validation/NEON/BitwiseOr.cpp new file mode 100644 index 0000000000..cb853d3fd4 --- /dev/null +++ b/tests/validation/NEON/BitwiseOr.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEBitwiseOr.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon bitwise Or function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_bitwise_or(const TensorShape &shape) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + // Create and configure function + NEBitwiseOr bor; + bor.configure(&src1, &src2, &dst); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src1), 0); + library->fill_tensor_uniform(NEAccessor(src2), 1); + + // Compute function + bor.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(BitwiseOr) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, SmallShapes() + LargeShapes(), shape) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEBitwiseOr bor; + bor.configure(&src1, &src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(src2.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + Tensor dst = compute_bitwise_or(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_or(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), shape) +{ + // Compute function + Tensor dst = compute_bitwise_or(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_or(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/BitwiseXor.cpp b/tests/validation/NEON/BitwiseXor.cpp new file mode 100644 index 0000000000..1715b04609 --- /dev/null +++ b/tests/validation/NEON/BitwiseXor.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEBitwiseXor.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon bitwise xor function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_bitwise_xor(const TensorShape &shape) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + // Create xor configure function + NEBitwiseXor bxor; + bxor.configure(&src1, &src2, &dst); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src1), 0); + library->fill_tensor_uniform(NEAccessor(src2), 1); + + // Compute function + bxor.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(BitwiseXor) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, SmallShapes() + LargeShapes(), shape) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create xor configure function + NEBitwiseXor bxor; + bxor.configure(&src1, &src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(src2.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + Tensor dst = compute_bitwise_xor(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_xor(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), shape) +{ + // Compute function + Tensor dst = compute_bitwise_xor(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_bitwise_xor(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Box3x3.cpp b/tests/validation/NEON/Box3x3.cpp new file mode 100644 index 0000000000..5da015c73a --- /dev/null +++ b/tests/validation/NEON/Box3x3.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEBox3x3.h" +#include "arm_compute/runtime/SubTensor.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon 3-by-3 box filter. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_box3x3(const TensorShape &shape) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + // Create and configure function + NEBox3x3 band; + band.configure(&src, &dst, BorderMode::UNDEFINED); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src), 0); + + // Compute function + band.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(Box3x3) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, SmallShapes() + LargeShapes(), shape) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEBox3x3 band; + band.configure(&src, &dst, BorderMode::UNDEFINED); + + // Validate valid region + const ValidRegion src_valid_region = shape_to_valid_region(shape); + const ValidRegion dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1)); + validate(src.info()->valid_region(), src_valid_region); + validate(dst.info()->valid_region(), dst_valid_region); + + // Validate padding + const PaddingSize read_padding(0, required_padding_undefined_border_read(shape.x(), 16, 8), 0, 0); + const PaddingSize write_padding(0, required_padding_undefined_border_write(shape.x(), 8, 1), 0, 0); + validate(src.info()->padding(), read_padding); + validate(dst.info()->padding(), write_padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + Tensor dst = compute_box3x3(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_box3x3(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst, shape_to_valid_region_undefined_border(shape, BorderSize(1))); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), shape) +{ + // Compute function + Tensor dst = compute_box3x3(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_box3x3(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst, shape_to_valid_region_undefined_border(shape, BorderSize(1))); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/CMakeLists.txt b/tests/validation/NEON/CMakeLists.txt new file mode 100644 index 0000000000..52678f345b --- /dev/null +++ b/tests/validation/NEON/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright (c) 2017 ARM Limited. +# +# SPDX-License-Identifier: MIT +# +# 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. +cmake_minimum_required (VERSION 3.1) + +set(arm_compute_test_validation_NEON_SOURCE_FILES + ${CMAKE_SOURCE_DIR}/NEON/Helper.h + ${CMAKE_SOURCE_DIR}/NEON/NEAccessor.h + ${CMAKE_CURRENT_SOURCE_DIR}/AbsoluteDifference.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Accumulate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/AccumulateSquared.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/AccumulateWeighted.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ArithmeticAddition.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ArithmeticSubtraction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BitwiseAnd.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BitwiseNot.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BitwiseOr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BitwiseXor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Box3x3.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Fixedpoint/Exp_QS8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Fixedpoint/Invsqrt_QS8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Fixedpoint/Log_QS8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Fixedpoint/Reciprocal_QS8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/NormalizationLayer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/PixelWiseMultiplication.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/IntegralImage.cpp +) + +add_library(arm_compute_test_validation_NEON OBJECT + ${arm_compute_test_validation_NEON_SOURCE_FILES} +) + +set(arm_compute_test_validation_TARGET_OBJECTS + ${arm_compute_test_validation_TARGET_OBJECTS} + $<TARGET_OBJECTS:arm_compute_test_validation_NEON> + PARENT_SCOPE +) diff --git a/tests/validation/NEON/ConvolutionLayer.cpp b/tests/validation/NEON/ConvolutionLayer.cpp new file mode 100644 index 0000000000..a1dbe38bbf --- /dev/null +++ b/tests/validation/NEON/ConvolutionLayer.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TypePrinter.h" +#include "dataset/ConvolutionLayerDataset.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Error.h" +#include "arm_compute/runtime/NEON/functions/NEConvolutionLayer.h" + +#include <random> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance_f32 = 1e-03f; /**< Tolerance value for comparing reference's output against implementation's output for DataType::F32 */ +const float tolerance_qs8 = 3.0f; /**< Tolerance value for comparing reference's output against implementation's output for DataType::QS8 */ + +Tensor compute_convolution_layer(const TensorShape &input_shape, const TensorShape &weights_shape, const TensorShape &bias_shape, const TensorShape &output_shape, DataType dt, + const PadStrideInfo &conv_info, int fixed_point_position) +{ + // Create tensors + Tensor src = create_tensor(input_shape, dt, 1, fixed_point_position); + Tensor weights = create_tensor(weights_shape, dt, 1, fixed_point_position); + Tensor bias = create_tensor(bias_shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(output_shape, dt, 1, fixed_point_position); + + // Create and configure function + NEConvolutionLayer conv; + conv.configure(&src, &weights, &bias, &dst, conv_info); + + // Allocate tensors + src.allocator()->allocate(); + weights.allocator()->allocate(); + bias.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!weights.info()->is_resizable()); + BOOST_TEST(!bias.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + if(dt == DataType::F32) + { + std::uniform_real_distribution<> distribution(-1.0f, 1.0f); + library->fill(NEAccessor(src), distribution, 0); + library->fill(NEAccessor(weights), distribution, 1); + library->fill(NEAccessor(bias), distribution, 2); + } + else + { + library->fill_tensor_uniform(NEAccessor(src), 0); + library->fill_tensor_uniform(NEAccessor(weights), 1); + library->fill_tensor_uniform(NEAccessor(bias), 2); + } + + // Compute NEConvolutionLayer function + conv.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(ConvolutionLayer) +BOOST_AUTO_TEST_SUITE(GEMM) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, + AlexNetConvolutionLayerDataset() * boost::unit_test::data::make({ DataType::F32, DataType::QS8 }), + conv_set, dt) +{ + // Set fixed point position data type allowed + int fixed_point_position = (dt == DataType::F32) ? 0 : 3; + + // Create tensors + Tensor src = create_tensor(conv_set.src_shape, dt, 1, fixed_point_position); + Tensor weights = create_tensor(conv_set.weights_shape, dt, 1, fixed_point_position); + Tensor bias = create_tensor(conv_set.bias_shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(conv_set.dst_shape, dt, 1, fixed_point_position); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(weights.info()->is_resizable()); + BOOST_TEST(bias.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEConvolutionLayer conv; + conv.configure(&src, &weights, &bias, &dst, conv_set.info); + + // Validate valid region + const ValidRegion src_valid_region = shape_to_valid_region(conv_set.src_shape); + const ValidRegion weights_valid_region = shape_to_valid_region(conv_set.weights_shape); + const ValidRegion bias_valid_region = shape_to_valid_region(conv_set.bias_shape); + const ValidRegion dst_valid_region = shape_to_valid_region(conv_set.dst_shape); + + validate(src.info()->valid_region(), src_valid_region); + validate(weights.info()->valid_region(), weights_valid_region); + validate(bias.info()->valid_region(), bias_valid_region); + validate(dst.info()->valid_region(), dst_valid_region); +} + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(SmallConvolutionLayer, + SmallConvolutionLayerDataset() * boost::unit_test::data::make(DataType::F32), + conv_set, dt) +{ + // Compute function + Tensor dst = compute_convolution_layer(conv_set.src_shape, conv_set.weights_shape, conv_set.bias_shape, conv_set.dst_shape, dt, conv_set.info, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_convolution_layer(conv_set.src_shape, conv_set.weights_shape, conv_set.bias_shape, conv_set.dst_shape, dt, conv_set.info, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_f32); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(LargeConvolutionLayer, + AlexNetConvolutionLayerDataset() * boost::unit_test::data::make(DataType::F32), + conv_set, dt) +{ + // Compute function + Tensor dst = compute_convolution_layer(conv_set.src_shape, conv_set.weights_shape, conv_set.bias_shape, conv_set.dst_shape, dt, conv_set.info, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_convolution_layer(conv_set.src_shape, conv_set.weights_shape, conv_set.bias_shape, conv_set.dst_shape, dt, conv_set.info, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_f32); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(Quantized) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(SmallConvolutionLayer, + SmallConvolutionLayerDataset() * boost::unit_test::data::make(DataType::QS8) * boost::unit_test::data::xrange(4, 7), + conv_set, dt, fixed_point_position) +{ + // Compute function + Tensor dst = compute_convolution_layer(conv_set.src_shape, conv_set.weights_shape, conv_set.bias_shape, conv_set.dst_shape, dt, conv_set.info, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_convolution_layer(conv_set.src_shape, conv_set.weights_shape, conv_set.bias_shape, conv_set.dst_shape, dt, conv_set.info, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_qs8); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(LargeConvolutionLayer, + AlexNetConvolutionLayerDataset() * boost::unit_test::data::make(DataType::QS8) * boost::unit_test::data::xrange(4, 7), + conv_set, dt, fixed_point_position) +{ + // Compute function + Tensor dst = compute_convolution_layer(conv_set.src_shape, conv_set.weights_shape, conv_set.bias_shape, conv_set.dst_shape, dt, conv_set.info, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_convolution_layer(conv_set.src_shape, conv_set.weights_shape, conv_set.bias_shape, conv_set.dst_shape, dt, conv_set.info, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_qs8); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif
\ No newline at end of file diff --git a/tests/validation/NEON/ConvolutionLayerDirect.cpp b/tests/validation/NEON/ConvolutionLayerDirect.cpp new file mode 100644 index 0000000000..4e36e331bd --- /dev/null +++ b/tests/validation/NEON/ConvolutionLayerDirect.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEDirectConvolutionLayer.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> +#include <tuple> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance_fp = 1e-3f; /**< Tolerance for floating point tests */ +const float tolerance_qs8 = 1; /**< Tolerance for fixed point tests */ + +/** Compute NEON direct convolution layer function. + * + * @param[in] src_shape Shape of the input tensor. + * @param[in] weights_shape Shape of the weights. + * @param[in] bias_shape Shape of the bias tensor. + * @param[in] dst_shape Shape of the output tensor. + * @param[in] dt Data type of input, convolution matrix and output tensors. + * @param[in] conv_info Padding and stride information. + * @param[in] fixed_point_position (Optional) Number of bits for the fractional part of the fixed point numbers + * + * @return Computed output tensor. +*/ +Tensor compute_convolution_layer(const TensorShape &src_shape, const TensorShape &weights_shape, const TensorShape &bias_shape, const TensorShape &dst_shape, + DataType dt, PadStrideInfo conv_info, int fixed_point_position = 0) +{ + // Create tensors + Tensor src = create_tensor(src_shape, dt, 1, fixed_point_position); + Tensor weights = create_tensor(weights_shape, dt, 1, fixed_point_position); + Tensor bias = create_tensor(bias_shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(dst_shape, dt, 1, fixed_point_position); + + // Create and configure function + NEDirectConvolutionLayer conv_layer; + conv_layer.configure(&src, &weights, &bias, &dst, conv_info); + + // Allocate tensors + src.allocator()->allocate(); + weights.allocator()->allocate(); + bias.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!weights.info()->is_resizable()); + BOOST_TEST(!bias.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + if(dt == DataType::F32) + { + std::uniform_real_distribution<> distribution(-1.f, 1.f); + library->fill(NEAccessor(src), distribution, 0); + library->fill(NEAccessor(weights), distribution, 1); + library->fill(NEAccessor(bias), distribution, 2); + } + else + { + library->fill_tensor_uniform(NEAccessor(src), 0); + library->fill_tensor_uniform(NEAccessor(weights), 1); + library->fill_tensor_uniform(NEAccessor(bias), 2); + } + + // Compute function + conv_layer.run(); + + return dst; +} + +TensorShape get_output_shape(TensorShape in_shape, TensorShape kernel_shape, const PadStrideInfo &conv_info) +{ + TensorShape out_shape(in_shape); + const std::pair<unsigned int, unsigned int> scaled_dims = arm_compute::scaled_dimensions(in_shape.x(), + in_shape.y(), + kernel_shape.x(), + conv_info.stride().first, conv_info.stride().second, + conv_info.pad().first, conv_info.pad().second, + conv_info.round()); + out_shape.set(0, scaled_dims.first); + out_shape.set(1, scaled_dims.second); + out_shape.set(2, kernel_shape[3]); + return out_shape; +} + +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(ConvolutionLayer) +BOOST_AUTO_TEST_SUITE(Direct) + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(W1x1, + DirectConvolutionShapes() * CNNFloatDataTypes() * boost::unit_test::data::xrange(1, 3, 1) * boost::unit_test::data::xrange(1, 3, 1) * boost::unit_test::data::make({ 1, 4, 8, 16 }), + input_shape, dt, sx, sy, num_kernels) +{ + const unsigned int kernel_size = 1; + const PadStrideInfo conv_info(sx, sy, 0, 0, DimensionRoundingType::FLOOR); + const TensorShape w_shape(kernel_size, kernel_size, input_shape.z(), static_cast<unsigned int>(num_kernels)); + const TensorShape b_shape(static_cast<unsigned int>(num_kernels)); + const TensorShape d_shape(get_output_shape(input_shape, w_shape, conv_info)); + + Tensor dst = compute_convolution_layer(input_shape, w_shape, b_shape, d_shape, dt, conv_info); + + RawTensor ref = Reference::compute_reference_convolution_layer(input_shape, w_shape, b_shape, d_shape, dt, conv_info, 0); + + // Validate output + validate(NEAccessor(dst), ref); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(W3x3, DirectConvolutionShapes() * CNNFloatDataTypes() * boost::unit_test::data::xrange(1, 3, 1) * boost::unit_test::data::xrange(1, 3, 1) * boost::unit_test::data::xrange(0, 2, + 1) + * boost::unit_test::data::xrange(0, 2, 1) * boost::unit_test::data::make({ 1, 4, 8, 16 }), + input_shape, dt, sx, sy, px, py, num_kernels) +{ + const unsigned int kernel_size = 3; + const PadStrideInfo conv_info(sx, sy, px, py, DimensionRoundingType::FLOOR); + const TensorShape w_shape(kernel_size, kernel_size, input_shape.z(), static_cast<unsigned int>(num_kernels)); + const TensorShape b_shape(static_cast<unsigned int>(num_kernels)); + const TensorShape d_shape(get_output_shape(input_shape, w_shape, conv_info)); + + Tensor dst = compute_convolution_layer(input_shape, w_shape, b_shape, d_shape, dt, conv_info); + + RawTensor ref = Reference::compute_reference_convolution_layer(input_shape, w_shape, b_shape, d_shape, dt, conv_info, 0); + + // Validate output + validate(NEAccessor(dst), ref, tolerance_fp); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(Quantized) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(W1x1, + DirectConvolutionShapes() * boost::unit_test::data::xrange(1, 3, 1) * boost::unit_test::data::xrange(1, 3, 1) * boost::unit_test::data::make({ 1, 4, 8, 16 }) * boost::unit_test::data::make({ 4, 5 }), + input_shape, sx, sy, num_kernels, fixed_point_position) +{ + const unsigned int kernel_size = 1; + const PadStrideInfo conv_info(sx, sy, 0, 0, DimensionRoundingType::FLOOR); + const TensorShape w_shape(kernel_size, kernel_size, input_shape.z(), static_cast<unsigned int>(num_kernels)); + const TensorShape b_shape(static_cast<unsigned int>(num_kernels)); + const TensorShape d_shape(get_output_shape(input_shape, w_shape, conv_info)); + + Tensor dst = compute_convolution_layer(input_shape, w_shape, b_shape, d_shape, DataType::QS8, conv_info, fixed_point_position); + + RawTensor ref = Reference::compute_reference_convolution_layer(input_shape, w_shape, b_shape, d_shape, DataType::QS8, conv_info, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(W3x3, DirectConvolutionShapes() * boost::unit_test::data::xrange(1, 3, 1) * boost::unit_test::data::xrange(1, 3, 1) * boost::unit_test::data::xrange(0, 2, 1) + * boost::unit_test::data::xrange(0, 2, 1) * boost::unit_test::data::make({ 1, 4, 8, 16 }) * boost::unit_test::data::make({ 4, 5 }), + input_shape, sx, sy, px, py, num_kernels, fixed_point_position) +{ + const unsigned int kernel_size = 3; + const PadStrideInfo conv_info(sx, sy, px, py, DimensionRoundingType::FLOOR); + const TensorShape w_shape(kernel_size, kernel_size, input_shape.z(), static_cast<unsigned int>(num_kernels)); + const TensorShape b_shape(static_cast<unsigned int>(num_kernels)); + const TensorShape d_shape(get_output_shape(input_shape, w_shape, conv_info)); + + Tensor dst = compute_convolution_layer(input_shape, w_shape, b_shape, d_shape, DataType::QS8, conv_info, fixed_point_position); + + RawTensor ref = Reference::compute_reference_convolution_layer(input_shape, w_shape, b_shape, d_shape, DataType::QS8, conv_info, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref, tolerance_qs8); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif
\ No newline at end of file diff --git a/tests/validation/NEON/DepthConvert.cpp b/tests/validation/NEON/DepthConvert.cpp new file mode 100644 index 0000000000..ec0bb7ccc5 --- /dev/null +++ b/tests/validation/NEON/DepthConvert.cpp @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEDepthConvert.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon depth convert function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in Data type of input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] policy Conversion policy. + * @param[in] shift Value for down/up conversions. Must be 0 <= shift < 8. + * @param[in] fixed_point_position Fixed point position. + * + * @return Computed output tensor. + */ +Tensor compute_depth_convert(const TensorShape &shape, DataType dt_in, DataType dt_out, ConvertPolicy policy, uint32_t shift, uint32_t fixed_point_position) +{ + // Create tensors + Tensor src = create_tensor(shape, dt_in, 1, fixed_point_position); + Tensor dst = create_tensor(shape, dt_out, 1, fixed_point_position); + + // Create and configure function + NEDepthConvert depth_convert; + depth_convert.configure(&src, &dst, policy, shift); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src), 0); + + // Compute function + depth_convert.run(); + + return dst; +} +/** Configure and validate region/padding function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in Data type of input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] policy Conversion policy. + * @param[in] shift Value for down/up conversions. Must be 0 <= shift < 8. + * @param[in] fixed_point_position Fixed point position. + * + */ + +void compute_configure_validate(const TensorShape &shape, DataType dt_in, DataType dt_out, ConvertPolicy policy, uint32_t shift, uint32_t fixed_point_position) +{ + // Create tensors + Tensor src = create_tensor(shape, dt_in, 1, fixed_point_position); + Tensor dst = create_tensor(shape, dt_out, 1, fixed_point_position); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEDepthConvert depth_convert; + depth_convert.configure(&src, &dst, policy, shift); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(DepthConvert) + +BOOST_AUTO_TEST_SUITE(QS8_to_F32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE }) + * boost::unit_test::data::xrange(1, 7, 1), + shape, policy, fixed_point_position) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::QS8, DataType::F32, policy, 0, fixed_point_position); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE }) + * boost::unit_test::data::xrange(1, 7, 1), + shape, policy, fixed_point_position) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::QS8, DataType::F32, policy, 0, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::QS8, DataType::F32, policy, 0, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE }) + * boost::unit_test::data::xrange(1, 7, 1), + shape, policy, fixed_point_position) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::QS8, DataType::F32, policy, 0, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::QS8, DataType::F32, policy, 0, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(F32_to_QS8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE }) + * boost::unit_test::data::xrange(1, 7, 1), + shape, policy, fixed_point_position) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::F32, DataType::QS8, policy, 0, fixed_point_position); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE }) + * boost::unit_test::data::xrange(1, 7, 1), + shape, policy, fixed_point_position) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::F32, DataType::QS8, policy, 0, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::F32, DataType::QS8, policy, 0, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE }) + * boost::unit_test::data::xrange(1, 7, 1), + shape, policy, fixed_point_position) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::F32, DataType::QS8, policy, 0, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::F32, DataType::QS8, policy, 0, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U8_to_U16) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) + +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U8, DataType::U16, policy, shift, 0); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U8, DataType::U16, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::U16, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U8, DataType::U16, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::U16, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U8_to_S16) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U8, DataType::S16, policy, shift, 0); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U8, DataType::S16, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::S16, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U8, DataType::S16, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::S16, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U8_to_S32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U8, DataType::S32, policy, shift, 0); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U8, DataType::S32, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::S32, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U8, DataType::S32, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U8, DataType::S32, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U16_to_U8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U16, DataType::U8, policy, shift, 0); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U16, DataType::U8, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U16, DataType::U8, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U16, DataType::U8, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U16, DataType::U8, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(U16_to_U32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::U16, DataType::U32, policy, shift, 0); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U16, DataType::U32, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U16, DataType::U32, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::U16, DataType::U32, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::U16, DataType::U32, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(S16_to_U8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::S16, DataType::U8, policy, shift, 0); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::S16, DataType::U8, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::S16, DataType::U8, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::S16, DataType::U8, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::S16, DataType::U8, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(S16_to_S32) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute configure and validate region/padding + compute_configure_validate(shape, DataType::S16, DataType::S32, policy, shift, 0); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::S16, DataType::S32, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::S16, DataType::S32, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ ConvertPolicy::SATURATE, ConvertPolicy::WRAP }) + * boost::unit_test::data::xrange(0, 7, 1), + shape, policy, shift) +{ + // Compute function + Tensor dst = compute_depth_convert(shape, DataType::S16, DataType::S32, policy, shift, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_depth_convert(shape, DataType::S16, DataType::S32, policy, shift, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/FillBorder.cpp b/tests/validation/NEON/FillBorder.cpp new file mode 100644 index 0000000000..9fbeb998f5 --- /dev/null +++ b/tests/validation/NEON/FillBorder.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/NEON/kernels/NEFillBorderKernel.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(FillBorder, BorderModes() * boost::unit_test::data::make({ PaddingSize{ 0 }, PaddingSize{ 1, 0, 1, 2 }, PaddingSize{ 10 } }), border_mode, padding) +{ + constexpr uint8_t border_value = 42U; + constexpr uint8_t tensor_value = 89U; + BorderSize border_size{ 5 }; + + // Create tensors + Tensor src = create_tensor(TensorShape{ 10U, 10U, 2U }, DataType::U8); + + src.info()->extend_padding(padding); + + // Allocate tensor + src.allocator()->allocate(); + + // Check padding is as required + validate(src.info()->padding(), padding); + + // Fill tensor with constant value + std::uniform_int_distribution<uint8_t> distribution{ tensor_value, tensor_value }; + library->fill(NEAccessor(src), distribution, 0); + + // Create and configure kernel + NEFillBorderKernel fill_border; + fill_border.configure(&src, border_size, border_mode, border_value); + + // Run kernel + fill_border.run(fill_border.window()); + + // Validate border + border_size.limit(padding); + validate(NEAccessor(src), border_size, border_mode, &border_value); + + // Validate tensor + validate(NEAccessor(src), &tensor_value); +} + +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Fixedpoint/Exp_QS8.cpp b/tests/validation/NEON/Fixedpoint/Exp_QS8.cpp new file mode 100644 index 0000000000..086314fdd3 --- /dev/null +++ b/tests/validation/NEON/Fixedpoint/Exp_QS8.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/ReferenceCPP.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/NEON/NEFixedPoint.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance = 0.0f; /**< Tolerance value for comparing reference's output against implementation's output */ + +/** Compute Neon exponential function for signed 8bit fixed point. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_exp_qs8(const TensorShape &shape, int fixed_point_position) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::QS8, 1, fixed_point_position); + Tensor dst = create_tensor(shape, DataType::QS8, 1, fixed_point_position); + + constexpr unsigned int num_elems_processed_per_iteration = 16; + Window window = calculate_max_window(*src.info(), Steps(num_elems_processed_per_iteration)); + AccessWindowHorizontal input_access(src.info(), 0, num_elems_processed_per_iteration); + AccessWindowHorizontal output_access(dst.info(), 0, num_elems_processed_per_iteration); + + update_window_and_padding(window, input_access, output_access); + output_access.set_valid_region(window, src.info()->valid_region()); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors. Keep the range between (1, (1 << (fixed_point_position - 1))) so the result won't + // overflow. E.g. e^7 = 1096, which cannot be represented in QS8 + std::uniform_int_distribution<> distribution(1, (1 << (fixed_point_position - 1))); + library->fill(NEAccessor(src), distribution, 0); + + Iterator input(&src, window); + Iterator output(&dst, window); + + execute_window_loop(window, [&](const Coordinates & id) + { + qint8x16_t in = vld1q_s8(reinterpret_cast<const qint8_t *>(input.ptr())); + // Use saturated exp + vst1q_s8(reinterpret_cast<qint8_t *>(output.ptr()), vqexpq_qs8(in, fixed_point_position)); + }, + input, output); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(FixedPoint) +BOOST_AUTO_TEST_SUITE(QS8) +BOOST_AUTO_TEST_SUITE(Exp) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunSmall, Small1DShape() * boost::unit_test::data::xrange(1, 7), shape, fixed_point_position) +{ + // Compute function + Tensor dst = compute_exp_qs8(shape, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fixed_point_operation(shape, DataType::QS8, DataType::QS8, FixedPointOp::EXP, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance, 0); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Fixedpoint/Invsqrt_QS8.cpp b/tests/validation/NEON/Fixedpoint/Invsqrt_QS8.cpp new file mode 100644 index 0000000000..3308f7d855 --- /dev/null +++ b/tests/validation/NEON/Fixedpoint/Invsqrt_QS8.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/ReferenceCPP.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/NEON/NEFixedPoint.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance = 3; /**< Tolerance value for comparing reference's output against implementation's output */ + +/** Compute Neon inverse square root function for signed 8bit fixed point. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_invsqrt_qs8(const TensorShape &shape, int fixed_point_position) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::QS8, 1, fixed_point_position); + Tensor dst = create_tensor(shape, DataType::QS8, 1, fixed_point_position); + + constexpr unsigned int num_elems_processed_per_iteration = 16; + Window window = calculate_max_window(*src.info(), Steps(num_elems_processed_per_iteration)); + AccessWindowHorizontal input_access(src.info(), 0, num_elems_processed_per_iteration); + AccessWindowHorizontal output_access(dst.info(), 0, num_elems_processed_per_iteration); + + update_window_and_padding(window, input_access, output_access); + output_access.set_valid_region(window, src.info()->valid_region()); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors. Keep the range between (32, 127) so the result won't + // overflow. E.g. for Q2.5 invsqrt(0.001) = 31.6, which cannot be represented. + std::uniform_int_distribution<> distribution(32, 127); + library->fill(NEAccessor(src), distribution, 0); + + Iterator input(&src, window); + Iterator output(&dst, window); + + execute_window_loop(window, [&](const Coordinates & id) + { + qint8x16_t in = vld1q_s8(reinterpret_cast<const qint8_t *>(input.ptr())); + vst1q_s8(reinterpret_cast<qint8_t *>(output.ptr()), vinvsqrtq_qs8(in, fixed_point_position)); + }, + input, output); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(FixedPoint) +BOOST_AUTO_TEST_SUITE(QS8) +BOOST_AUTO_TEST_SUITE(Invsqrt) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Small1DShape, SmallShapes() * boost::unit_test::data::xrange(1, 6), shape, fixed_point_position) +{ + // Compute function + Tensor dst = compute_invsqrt_qs8(shape, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fixed_point_operation(shape, DataType::QS8, DataType::QS8, FixedPointOp::INV_SQRT, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance, 0); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Fixedpoint/Log_QS8.cpp b/tests/validation/NEON/Fixedpoint/Log_QS8.cpp new file mode 100644 index 0000000000..7b734c12b1 --- /dev/null +++ b/tests/validation/NEON/Fixedpoint/Log_QS8.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/ReferenceCPP.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/NEON/NEFixedPoint.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance = 5; /**< Tolerance value for comparing reference's output against implementation's output */ + +/** Compute Neon logarithm function for signed 8bit fixed point. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_log_qs8(const TensorShape &shape, int fixed_point_position) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::QS8, 1, fixed_point_position); + Tensor dst = create_tensor(shape, DataType::QS8, 1, fixed_point_position); + + constexpr unsigned int num_elems_processed_per_iteration = 16; + Window window = calculate_max_window(*src.info(), Steps(num_elems_processed_per_iteration)); + AccessWindowHorizontal input_access(src.info(), 0, num_elems_processed_per_iteration); + AccessWindowHorizontal output_access(dst.info(), 0, num_elems_processed_per_iteration); + + update_window_and_padding(window, input_access, output_access); + output_access.set_valid_region(window, src.info()->valid_region()); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors. Keep the range between ((1 << (fixed_point_position - 1), 63) so the result won't + // overflow. E.g. for Q2.5 ln(0.001) = -6.9, which cannot be represented. + std::uniform_int_distribution<> distribution((1 << (fixed_point_position - 1)), 63); + library->fill(NEAccessor(src), distribution, 0); + + Iterator input(&src, window); + Iterator output(&dst, window); + + execute_window_loop(window, [&](const Coordinates & id) + { + qint8x16_t in = vld1q_s8(reinterpret_cast<const qint8_t *>(input.ptr())); + vst1q_s8(reinterpret_cast<qint8_t *>(output.ptr()), vlogq_qs8(in, fixed_point_position)); + }, + input, output); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(FixedPoint) +BOOST_AUTO_TEST_SUITE(QS8) +BOOST_AUTO_TEST_SUITE(Log) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunSmall, Small1DShape() * boost::unit_test::data::xrange(3, 6), shape, fixed_point_position) +{ + // Compute function + Tensor dst = compute_log_qs8(shape, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fixed_point_operation(shape, DataType::QS8, DataType::QS8, FixedPointOp::LOG, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance, 0); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Fixedpoint/Reciprocal_QS8.cpp b/tests/validation/NEON/Fixedpoint/Reciprocal_QS8.cpp new file mode 100644 index 0000000000..4c1c782a18 --- /dev/null +++ b/tests/validation/NEON/Fixedpoint/Reciprocal_QS8.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/ReferenceCPP.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/NEON/NEFixedPoint.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance = 3; /**< Tolerance value for comparing reference's output against implementation's output */ + +/** Compute Neon reciprocal function for signed 8bit fixed point. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_reciprocal_qs8(const TensorShape &shape, int fixed_point_position) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::QS8, 1, fixed_point_position); + Tensor dst = create_tensor(shape, DataType::QS8, 1, fixed_point_position); + + constexpr unsigned int num_elems_processed_per_iteration = 16; + Window window = calculate_max_window(*src.info(), Steps(num_elems_processed_per_iteration)); + AccessWindowHorizontal input_access(src.info(), 0, num_elems_processed_per_iteration); + AccessWindowHorizontal output_access(dst.info(), 0, num_elems_processed_per_iteration); + + update_window_and_padding(window, input_access, output_access); + output_access.set_valid_region(window, src.info()->valid_region()); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors. Keep the range between (15, 100) so the result won't + // overflow. E.g. for Q2.5 reciprocal(0.001) = 1000, which cannot be represented. + std::uniform_int_distribution<> distribution(15, 100); + library->fill(NEAccessor(src), distribution, 0); + + Iterator input(&src, window); + Iterator output(&dst, window); + + execute_window_loop(window, [&](const Coordinates & id) + { + qint8x16_t in = vld1q_s8(reinterpret_cast<const qint8_t *>(input.ptr())); + vst1q_s8(reinterpret_cast<qint8_t *>(output.ptr()), vrecipq_qs8(in, fixed_point_position)); + }, + input, output); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(FixedPoint) +BOOST_AUTO_TEST_SUITE(QS8) +BOOST_AUTO_TEST_SUITE(Reciprocal) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunSmall, Small1DShape() * boost::unit_test::data::xrange(1, 6), shape, fixed_point_position) +{ + // Compute function + Tensor dst = compute_reciprocal_qs8(shape, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fixed_point_operation(shape, DataType::QS8, DataType::QS8, FixedPointOp::RECIPROCAL, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance, 0); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/FullyConnectedLayer.cpp b/tests/validation/NEON/FullyConnectedLayer.cpp new file mode 100644 index 0000000000..bda235bd55 --- /dev/null +++ b/tests/validation/NEON/FullyConnectedLayer.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TypePrinter.h" +#include "dataset/FullyConnectedLayerDataset.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Error.h" +#include "arm_compute/core/Helpers.h" +#include "arm_compute/runtime/NEON/functions/NEFullyConnectedLayer.h" + +#include <random> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance_f32 = 1e-03f; /**< Tolerance value for comparing reference's output against implementation's output for DataType::F32 */ +const float tolerance_qs8 = 1.0f; /**< Tolerance value for comparing reference's output against implementation's output for DataType::QS8 */ + +Tensor compute_fully_connected_layer(const TensorShape &input_shape, const TensorShape &weights_shape, const TensorShape &bias_shape, const TensorShape &output_shape, DataType dt, + bool transpose_weights, int fixed_point_position) +{ + // Create tensors + Tensor src = create_tensor(input_shape, dt, 1, fixed_point_position); + Tensor bias = create_tensor(bias_shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(output_shape, dt, 1, fixed_point_position); + + // Swap the first and second dimension of weights' shape if transpose_weights is true + TensorShape ws = weights_shape; + if(transpose_weights) + { + const size_t dimx = ws.x(); + ws.set(0, ws.y()); + ws.set(1, dimx); + } + + Tensor weights = create_tensor(ws, dt, 1, fixed_point_position); + + // Create and configure function. + // Note: We pass the weights already transposed + NEFullyConnectedLayer fc; + fc.configure(&src, &weights, &bias, &dst, false); + + // Allocate tensors + src.allocator()->allocate(); + weights.allocator()->allocate(); + bias.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!weights.info()->is_resizable()); + BOOST_TEST(!bias.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + if(dt == DataType::F32) + { + std::uniform_real_distribution<> distribution(-1.0f, 1.0f); + library->fill(NEAccessor(src), distribution, 0); + library->fill(NEAccessor(weights), distribution, 1); + library->fill(NEAccessor(bias), distribution, 2); + } + else + { + library->fill_tensor_uniform(NEAccessor(src), 0); + library->fill_tensor_uniform(NEAccessor(weights), 1); + library->fill_tensor_uniform(NEAccessor(bias), 2); + } + + // Compute NEFullyConnectedLayer function + fc.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(FullyConnectedLayer) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, + SmallFullyConnectedLayerDataset() * boost::unit_test::data::make({ DataType::F32, DataType::QS8 }), + fc_set, dt) +{ + // Set fixed point position data type allowed + int fixed_point_position = (dt == DataType::F32) ? 0 : 3; + + // Create tensors + Tensor src = create_tensor(fc_set.src_shape, dt, 1, fixed_point_position); + Tensor bias = create_tensor(fc_set.bias_shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(fc_set.dst_shape, dt, 1, fixed_point_position); + + // Swap the first and second dimension of weights' shape if transpose_weights is true + TensorShape ws = fc_set.weights_shape; + if(fc_set.transpose_weights) + { + const size_t dimx = ws.x(); + ws.set(0, ws.y()); + ws.set(1, dimx); + } + + Tensor weights = create_tensor(ws, dt, 1, fixed_point_position); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(weights.info()->is_resizable()); + BOOST_TEST(bias.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function. + // Note: We pass the weights already transposed + NEFullyConnectedLayer fc; + fc.configure(&src, &weights, &bias, &dst, false); + + // Validate valid region + const ValidRegion src_valid_region = shape_to_valid_region(fc_set.src_shape); + const ValidRegion weights_valid_region = shape_to_valid_region(ws); + const ValidRegion bias_valid_region = shape_to_valid_region(fc_set.bias_shape); + const ValidRegion dst_valid_region = shape_to_valid_region(fc_set.dst_shape); + + validate(src.info()->valid_region(), src_valid_region); + validate(weights.info()->valid_region(), weights_valid_region); + validate(bias.info()->valid_region(), bias_valid_region); + validate(dst.info()->valid_region(), dst_valid_region); +} + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, + SmallFullyConnectedLayerDataset() * boost::unit_test::data::make({ DataType::F32 }), + fc_set, dt) +{ + // Compute function + Tensor dst = compute_fully_connected_layer(fc_set.src_shape, fc_set.weights_shape, fc_set.bias_shape, fc_set.dst_shape, dt, fc_set.transpose_weights, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fully_connected_layer(fc_set.src_shape, fc_set.weights_shape, fc_set.bias_shape, fc_set.dst_shape, dt, fc_set.transpose_weights, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_f32); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, + LargeFullyConnectedLayerDataset() * boost::unit_test::data::make({ DataType::F32 }), + fc_set, dt) +{ + // Compute function + Tensor dst = compute_fully_connected_layer(fc_set.src_shape, fc_set.weights_shape, fc_set.bias_shape, fc_set.dst_shape, dt, fc_set.transpose_weights, 0); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fully_connected_layer(fc_set.src_shape, fc_set.weights_shape, fc_set.bias_shape, fc_set.dst_shape, dt, fc_set.transpose_weights, 0); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_f32); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(Quantized) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, + SmallFullyConnectedLayerDataset() * boost::unit_test::data::make({ DataType::QS8 }) * boost::unit_test::data::xrange(4, 7), + fc_set, dt, fixed_point_position) +{ + // Compute function + Tensor dst = compute_fully_connected_layer(fc_set.src_shape, fc_set.weights_shape, fc_set.bias_shape, fc_set.dst_shape, dt, fc_set.transpose_weights, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fully_connected_layer(fc_set.src_shape, fc_set.weights_shape, fc_set.bias_shape, fc_set.dst_shape, dt, fc_set.transpose_weights, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_qs8); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, + LargeFullyConnectedLayerDataset() * boost::unit_test::data::make({ DataType::QS8 }) * boost::unit_test::data::xrange(4, 7), + fc_set, dt, fixed_point_position) +{ + // Compute function + Tensor dst = compute_fully_connected_layer(fc_set.src_shape, fc_set.weights_shape, fc_set.bias_shape, fc_set.dst_shape, dt, fc_set.transpose_weights, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fully_connected_layer(fc_set.src_shape, fc_set.weights_shape, fc_set.bias_shape, fc_set.dst_shape, dt, fc_set.transpose_weights, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_qs8); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/GEMM.cpp b/tests/validation/NEON/GEMM.cpp new file mode 100644 index 0000000000..0172ddeb76 --- /dev/null +++ b/tests/validation/NEON/GEMM.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "dataset/GEMMDataset.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEGEMM.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance_f32 = 1e-03f; /**< Tolerance value for comparing reference's output against implementation's output for DataType::F32 */ +const float tolerance_qs8 = 1.0f; /**< Tolerance value for comparing reference's output against implementation's output for DataType::QS8 */ + +Tensor compute_gemm(const TensorShape &src_shape1, const TensorShape &src_shape2, const TensorShape &src_shape3, + const TensorShape &out_shape, float alpha, float beta, DataType dt, int fixed_point_position = 0) +{ + // Create tensors + Tensor src1 = create_tensor(src_shape1, dt, 1, fixed_point_position); + Tensor src2 = create_tensor(src_shape2, dt, 1, fixed_point_position); + Tensor src3 = create_tensor(src_shape3, dt, 1, fixed_point_position); + Tensor dst = create_tensor(out_shape, dt, 1, fixed_point_position); + + // Create and configure function + NEGEMM gemm; + gemm.configure(&src1, &src2, &src3, &dst, alpha, beta); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + src3.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!src3.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + if(dt == DataType::F32) + { + std::uniform_real_distribution<> distribution(-1.0f, 1.0f); + library->fill(NEAccessor(src1), distribution, 0); + library->fill(NEAccessor(src2), distribution, 1); + library->fill(NEAccessor(src3), distribution, 2); + } + else + { + library->fill_tensor_uniform(NEAccessor(src1), 0); + library->fill_tensor_uniform(NEAccessor(src2), 1); + library->fill_tensor_uniform(NEAccessor(src3), 2); + } + + // Compute function + gemm.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(GEMM) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, + SmallGEMMDataset() * boost::unit_test::data::make({ DataType::F32, DataType::QS8 }), + gemm_set, dt) +{ + // Set fixed point position data type allowed + int fixed_point_position = (dt == DataType::F32) ? 0 : 3; + + // Create tensors + Tensor src1 = create_tensor(gemm_set.shape_a, dt, 1, fixed_point_position); + Tensor src2 = create_tensor(gemm_set.shape_b, dt, 1, fixed_point_position); + Tensor src3 = create_tensor(gemm_set.shape_c, dt, 1, fixed_point_position); + Tensor dst = create_tensor(gemm_set.shape_d, dt, 1, fixed_point_position); + + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(src3.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEGEMM gemm; + gemm.configure(&src1, &src2, &src3, &dst, gemm_set.alpha, gemm_set.beta); + + // Validate valid region + const ValidRegion src1_valid_region = shape_to_valid_region(gemm_set.shape_a); + const ValidRegion src2_valid_region = shape_to_valid_region(gemm_set.shape_b); + const ValidRegion src3_valid_region = shape_to_valid_region(gemm_set.shape_c); + const ValidRegion dst_valid_region = shape_to_valid_region(gemm_set.shape_d); + + validate(src1.info()->valid_region(), src1_valid_region); + validate(src2.info()->valid_region(), src2_valid_region); + validate(src3.info()->valid_region(), src3_valid_region); + validate(dst.info()->valid_region(), dst_valid_region); +} + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(SmallGEMM, SmallGEMMDataset() * boost::unit_test::data::make(DataType::F32), + gemm_set, dt) +{ + // Compute reference + RawTensor ref_dst = Reference::compute_reference_gemm(gemm_set.shape_a, gemm_set.shape_b, gemm_set.shape_c, gemm_set.shape_d, gemm_set.alpha, gemm_set.beta, dt); + + // Compute function + Tensor dst = compute_gemm(gemm_set.shape_a, gemm_set.shape_b, gemm_set.shape_c, gemm_set.shape_d, gemm_set.alpha, gemm_set.beta, dt); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_f32); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(LargeGEMM, LargeGEMMDataset() * boost::unit_test::data::make(DataType::F32), + gemm_set, dt) +{ + // Compute reference + RawTensor ref_dst = Reference::compute_reference_gemm(gemm_set.shape_a, gemm_set.shape_b, gemm_set.shape_c, gemm_set.shape_d, gemm_set.alpha, gemm_set.beta, dt); + + // Compute function + Tensor dst = compute_gemm(gemm_set.shape_a, gemm_set.shape_b, gemm_set.shape_c, gemm_set.shape_d, gemm_set.alpha, gemm_set.beta, dt); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_f32); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(Quantized) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(SmallGEMM, SmallGEMMDataset() * boost::unit_test::data::make(DataType::QS8) * boost::unit_test::data::xrange(1, 7), + gemm_set, dt, fixed_point_position) +{ + // Compute reference + RawTensor ref_dst = Reference::compute_reference_gemm(gemm_set.shape_a, gemm_set.shape_b, gemm_set.shape_c, gemm_set.shape_d, gemm_set.alpha, gemm_set.beta, dt, fixed_point_position); + + // Compute function + Tensor dst = compute_gemm(gemm_set.shape_a, gemm_set.shape_b, gemm_set.shape_c, gemm_set.shape_d, gemm_set.alpha, gemm_set.beta, dt, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_qs8); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(LargeGEMM, LargeGEMMDataset() * boost::unit_test::data::make(DataType::QS8) * boost::unit_test::data::xrange(1, 7), + gemm_set, dt, fixed_point_position) +{ + // Compute reference + RawTensor ref_dst = Reference::compute_reference_gemm(gemm_set.shape_a, gemm_set.shape_b, gemm_set.shape_c, gemm_set.shape_d, gemm_set.alpha, gemm_set.beta, dt, fixed_point_position); + + // Compute function + Tensor dst = compute_gemm(gemm_set.shape_a, gemm_set.shape_b, gemm_set.shape_c, gemm_set.shape_d, gemm_set.alpha, gemm_set.beta, dt, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_qs8); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/IntegralImage.cpp b/tests/validation/NEON/IntegralImage.cpp new file mode 100644 index 0000000000..f94af430d1 --- /dev/null +++ b/tests/validation/NEON/IntegralImage.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEIntegralImage.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon integral image function. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed output tensor. + */ +Tensor compute_integral_image(const TensorShape &shape) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U32); + + // Create integral image configure function + NEIntegralImage integral_image; + integral_image.configure(&src, &dst); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src), 0); + + // Compute function + integral_image.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(IntegralImage) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, SmallShapes() + LargeShapes(), shape) +{ + // Create tensors + Tensor src = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U32); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create integral image configure function + NEIntegralImage integral_image; + integral_image.configure(&src, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize src_padding(0, required_padding(shape.x(), 16), 0, 0); + const PaddingSize dst_padding(1, required_padding(shape.x(), 16), 0, 1); + + validate(src.info()->padding(), src_padding); + validate(dst.info()->padding(), dst_padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes(), shape) +{ + // Compute function + Tensor dst = compute_integral_image(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_integral_image(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes(), shape) +{ + // Compute function + Tensor dst = compute_integral_image(shape); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_integral_image(shape); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/NormalizationLayer.cpp b/tests/validation/NEON/NormalizationLayer.cpp new file mode 100644 index 0000000000..ff791effa0 --- /dev/null +++ b/tests/validation/NEON/NormalizationLayer.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TypePrinter.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/runtime/NEON/functions/NENormalizationLayer.h" + +#include <random> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Define tolerance of the normalization layer depending on values data type. + * + * @param[in] dt Data type of the tensors' values. + * + * @return Tolerance depending on the data type. + */ +float normalization_layer_tolerance(DataType dt) +{ + switch(dt) + { + case DataType::QS8: + return 2.0f; + case DataType::F32: + return 1e-05; + default: + return 0.f; + } +} + +/** Compute Neon normalization layer function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt Data type of input and output tensors. + * @param[in] norm_info Normalization Layer information. + * @param[in] fixed_point_position (Optional) Fixed point position that expresses the number of bits for the fractional part of the number when the tensor's data type is QS8 or QS16 (default = 0). + * + * @return Computed output tensor. + */ +Tensor compute_normalization_layer(const TensorShape &shape, DataType dt, NormalizationLayerInfo norm_info, int fixed_point_position = 0) +{ + // Create tensors + Tensor src = create_tensor(shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(shape, dt, 1, fixed_point_position); + + // Create and configure function + NENormalizationLayer norm; + norm.configure(&src, &dst, norm_info); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + if(dt == DataType::QS8) + { + const int8_t one_fixed_point = 1 << fixed_point_position; + const int8_t minus_one_fixed_point = -one_fixed_point; + library->fill_tensor_uniform(NEAccessor(src), 0, minus_one_fixed_point, one_fixed_point); + } + else + { + library->fill_tensor_uniform(NEAccessor(src), 0); + } + + // Compute function + norm.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(NormalizationLayer) + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, + SmallShapes() * DataType::F32 *NormalizationTypes() * boost::unit_test::data::xrange(3, 9, 2) * boost::unit_test::data::make({ 0.5f, 1.0f, 2.0f }), + shape, dt, norm_type, norm_size, beta) +{ + // Provide normalization layer information + NormalizationLayerInfo norm_info(norm_type, norm_size, 5, beta); + + // Compute function + Tensor dst = compute_normalization_layer(shape, dt, norm_info); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_normalization_layer(shape, dt, norm_info); + + // Validate output + validate(NEAccessor(dst), ref_dst, normalization_layer_tolerance(DataType::F32)); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(Quantized) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, + SmallShapes() * DataType::QS8 *NormalizationTypes() * boost::unit_test::data::xrange(3, 7, 2) * (boost::unit_test::data::xrange(1, 6) * boost::unit_test::data::make({ 0.5f, 1.0f, 2.0f })), + shape, dt, norm_type, norm_size, fixed_point_position, beta) +{ + // Provide normalization layer information + NormalizationLayerInfo norm_info(norm_type, norm_size, 5, beta, 1.f); + + // Compute function + Tensor dst = compute_normalization_layer(shape, dt, norm_info, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_normalization_layer(shape, dt, norm_info, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, normalization_layer_tolerance(DataType::QS8)); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/PixelWiseMultiplication.cpp b/tests/validation/NEON/PixelWiseMultiplication.cpp new file mode 100644 index 0000000000..c6c2792126 --- /dev/null +++ b/tests/validation/NEON/PixelWiseMultiplication.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEPixelWiseMultiplication.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Neon arithmetic addition function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] scale Non-negative scale. + * @param[in] convert_policy Overflow policy of the operation. + * @param[in] rounding_policy Rounding policy of the operation. + * @param[in] fixed_point_position Fixed point position that expresses the number of bits for the fractional part of the number. + * + * @return Computed output tensor. + */ +Tensor compute_pixel_wise_multiplication(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, + int fixed_point_position = 0) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt_in0, 1, fixed_point_position); + Tensor src2 = create_tensor(shape, dt_in1, 1, fixed_point_position); + Tensor dst = create_tensor(shape, dt_out, 1, fixed_point_position); + + // Create and configure function + NEPixelWiseMultiplication multiply; + multiply.configure(&src1, &src2, &dst, scale, convert_policy, rounding_policy); + + // Allocate tensors + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!src2.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src1), 0); + library->fill_tensor_uniform(NEAccessor(src2), 1); + + // Compute function + multiply.run(); + + return dst; +} + +void validate_configuration(const Tensor &src1, const Tensor &src2, Tensor &dst, TensorShape shape, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) +{ + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(src2.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEPixelWiseMultiplication multiply; + multiply.configure(&src1, &src2, &dst, scale, convert_policy, rounding_policy); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(src2.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(PixelWiseMultiplication) + +BOOST_AUTO_TEST_SUITE(U8) + +BOOST_AUTO_TEST_SUITE(Scale255) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * (1.f / 255.f) * ConvertPolicies() + * RoundingPolicy::TO_NEAREST_UP, + shape, scale, convert_policy, rounding_policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + validate_configuration(src1, src2, dst, shape, scale, convert_policy, rounding_policy); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * (1.f / 255.f) * ConvertPolicies() * RoundingPolicy::TO_NEAREST_UP, + shape, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, DataType::U8, DataType::U8, DataType::U8, scale, convert_policy, + rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, DataType::U8, DataType::U8, + DataType::U8, scale, convert_policy, rounding_policy); + + // Validate output + // Allow tolerance value of 1.f to counteract imprecision due to 32-bit float conversion + validate(NEAccessor(dst), ref_dst, 1.f, 0.f, std::numeric_limits<uint8_t>::max()); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * (1.f / 255.f) * ConvertPolicies() * RoundingPolicy::TO_NEAREST_UP, + shape, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, DataType::U8, DataType::U8, DataType::U8, scale, convert_policy, + rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, DataType::U8, DataType::U8, + DataType::U8, scale, convert_policy, rounding_policy); + + // Validate output + // Allow tolerance value of 1.f to counteract imprecision due to 32-bit float conversion + validate(NEAccessor(dst), ref_dst, 1.f, 0.f, std::numeric_limits<uint8_t>::max()); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(ScaleOther) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) + * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, scale, convert_policy, rounding_policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor src2 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + validate_configuration(src1, src2, dst, shape, scale, convert_policy, rounding_policy); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, DataType::U8, DataType::U8, DataType::U8, scale, convert_policy, + rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, DataType::U8, DataType::U8, + DataType::U8, scale, convert_policy, rounding_policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, DataType::U8, DataType::U8, DataType::U8, scale, convert_policy, + rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, DataType::U8, DataType::U8, + DataType::U8, scale, convert_policy, rounding_policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(S16) +BOOST_AUTO_TEST_SUITE(Scale255) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * (1.f / 255.f) * ConvertPolicies() + * RoundingPolicy::TO_NEAREST_UP, + shape, dt, scale, convert_policy, rounding_policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt); + Tensor src2 = create_tensor(shape, DataType::S16); + Tensor dst = create_tensor(shape, DataType::S16); + + validate_configuration(src1, src2, dst, shape, scale, convert_policy, rounding_policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * (1.f / 255.f) * ConvertPolicies() + * RoundingPolicy::TO_NEAREST_UP, + shape, dt, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, dt, DataType::S16, DataType::S16, scale, convert_policy, rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, dt, DataType::S16, DataType::S16, scale, convert_policy, rounding_policy); + + // Validate output + // Allow tolerance value of 2.f to counteract imprecision due to 32-bit float conversion + validate(NEAccessor(dst), ref_dst, 2.f, 0.f, std::numeric_limits<int16_t>::max()); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * (1.f / 255.f) * ConvertPolicies() + * RoundingPolicy::TO_NEAREST_UP, + shape, dt, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, dt, DataType::S16, DataType::S16, scale, convert_policy, rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, dt, DataType::S16, DataType::S16, + scale, convert_policy, rounding_policy); + + // Validate output + // Allow tolerance value of 2.f to counteract imprecision due to 32-bit float conversion + validate(NEAccessor(dst), ref_dst, 2.f, 0.f, std::numeric_limits<int16_t>::max()); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(ScaleOther) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) + * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, dt, scale, convert_policy, rounding_policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, dt); + Tensor src2 = create_tensor(shape, DataType::S16); + Tensor dst = create_tensor(shape, DataType::S16); + + validate_configuration(src1, src2, dst, shape, scale, convert_policy, rounding_policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, dt, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, dt, DataType::S16, DataType::S16, scale, convert_policy, rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, dt, DataType::S16, DataType::S16, scale, convert_policy, rounding_policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ DataType::U8, DataType::S16 }) * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, dt, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, dt, DataType::S16, DataType::S16, scale, convert_policy, rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, dt, DataType::S16, DataType::S16, + scale, convert_policy, rounding_policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(F32) +BOOST_AUTO_TEST_SUITE(Scale255) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * (1.f / 255.f) * ConvertPolicies() + * RoundingPolicy::TO_NEAREST_UP, + shape, scale, convert_policy, rounding_policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::F32); + Tensor src2 = create_tensor(shape, DataType::F32); + Tensor dst = create_tensor(shape, DataType::F32); + + validate_configuration(src1, src2, dst, shape, scale, convert_policy, rounding_policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * (1.f / 255.f) * ConvertPolicies() + * RoundingPolicy::TO_NEAREST_UP, + shape, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, DataType::F32, DataType::F32, DataType::F32, scale, convert_policy, rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, DataType::F32, DataType::F32, DataType::F32, scale, convert_policy, rounding_policy); + + // Validate output + // Allow tolerance value of 1.f to counteract imprecision due to 32-bit float conversion + validate(NEAccessor(dst), ref_dst, 1.f, 0.f, std::numeric_limits<int16_t>::max()); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * (1.f / 255.f) * ConvertPolicies() + * RoundingPolicy::TO_NEAREST_UP, + shape, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, DataType::F32, DataType::F32, DataType::F32, scale, convert_policy, rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, DataType::F32, DataType::F32, DataType::F32, + scale, convert_policy, rounding_policy); + + // Validate output + // Allow tolerance value of 1.f to counteract imprecision due to 32-bit float conversion + validate(NEAccessor(dst), ref_dst, 1.f, 0.f, std::numeric_limits<int16_t>::max()); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(ScaleOther) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) + * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, scale, convert_policy, rounding_policy) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::F32); + Tensor src2 = create_tensor(shape, DataType::F32); + Tensor dst = create_tensor(shape, DataType::F32); + + validate_configuration(src1, src2, dst, shape, scale, convert_policy, rounding_policy); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, DataType::F32, DataType::F32, DataType::F32, scale, convert_policy, rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, DataType::F32, DataType::F32, DataType::F32, scale, convert_policy, rounding_policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * boost::unit_test::data::make({ 1.f, 1.f / 32768.f }) * ConvertPolicies() + * RoundingPolicy::TO_ZERO, + shape, scale, convert_policy, rounding_policy) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, DataType::F32, DataType::F32, DataType::F32, scale, convert_policy, rounding_policy); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pixel_wise_multiplication(shape, DataType::F32, DataType::F32, DataType::F32, + scale, convert_policy, rounding_policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(QS8) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * DataType::QS8 *ConvertPolicies() * RoundingPolicy::TO_ZERO * boost::unit_test::data::xrange<int>(1, 7), + shape, dt, convert_policy, rounding_policy, fixed_point_position) +{ + // Compute function + Tensor dst = compute_pixel_wise_multiplication(shape, dt, dt, dt, 1.f, convert_policy, rounding_policy, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_fixed_point_pixel_wise_multiplication(shape, dt, dt, dt, 1.f, fixed_point_position, convert_policy, rounding_policy); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Pooling/PoolingLayer.cpp b/tests/validation/NEON/Pooling/PoolingLayer.cpp new file mode 100644 index 0000000000..b15ad1c5e6 --- /dev/null +++ b/tests/validation/NEON/Pooling/PoolingLayer.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TypePrinter.h" +#include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h" +#include "tests/dataset/PoolingLayerDataset.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include <iostream> +#include <random> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +const float tolerance_q = 0; /**< Tolerance value for comparing reference's output against implementation's output for quantized input */ +const float tolerance_f = 1e-05; /**< Tolerance value for comparing reference's output against implementation's output for float input */ + +/** Compute Neon pooling layer function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt Data type of input and output tensors. + * @param[in] pool_info Pooling Layer information. + * + * @return Computed output tensor. + */ +Tensor compute_pooling_layer(const TensorShape &shape_in, const TensorShape &shape_out, DataType dt, PoolingLayerInfo pool_info, int fixed_point_position = 0) +{ + // Create tensors + Tensor src = create_tensor(shape_in, dt, 1, fixed_point_position); + Tensor dst = create_tensor(shape_out, dt, 1, fixed_point_position); + + // Create and configure function + NEPoolingLayer pool; + pool.configure(&src, &dst, pool_info); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + int min = 0; + int max = 0; + switch(dt) + { + case DataType::F32: + min = -1; + max = 1; + break; + case DataType::QS8: + min = -(1 << fixed_point_position); + max = (1 << fixed_point_position); + break; + default: + ARM_COMPUTE_ERROR("DataType not supported."); + } + std::uniform_real_distribution<> distribution(min, max); + library->fill(NEAccessor(src), distribution, 0); + + // Compute function + pool.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(Pooling) +BOOST_AUTO_TEST_SUITE(PoolingLayer) + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RandomDataset, + RandomPoolingLayerDataset() * boost::unit_test::data::make(DataType::F32), + obj, dt) +{ + // Compute function + Tensor dst = compute_pooling_layer(obj.src_shape, obj.dst_shape, dt, obj.info); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pooling_layer(obj.src_shape, obj.dst_shape, dt, obj.info); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_f, 0); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(Quantized) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RandomDataset, + RandomPoolingLayerDataset() * boost::unit_test::data::make(DataType::QS8) * boost::unit_test::data::xrange(1, 5), + obj, dt, fixed_point_position) +{ + // Compute function + Tensor dst = compute_pooling_layer(obj.src_shape, obj.dst_shape, dt, obj.info, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_pooling_layer(obj.src_shape, obj.dst_shape, dt, obj.info, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_q, 0); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/SoftmaxLayer.cpp b/tests/validation/NEON/SoftmaxLayer.cpp new file mode 100644 index 0000000000..f5c7a21abd --- /dev/null +++ b/tests/validation/NEON/SoftmaxLayer.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NESoftmaxLayer.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Tolerance for float operations */ +const float tolerance = 0.000001f; +/** Tolerance for fixed point operations */ +const float tolerance_fixed_point = 2.f; + +/** Compute Neon softmax layer function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt Shape Data type of tensors. + * @param[in] fixed_point_position (Optional) Number of bits for the fractional part of fixed point numbers. + * + * @return Computed output tensor. + */ +Tensor compute_softmax_layer(const TensorShape &shape, DataType dt, int fixed_point_position = 0) +{ + // Create tensors + Tensor src = create_tensor(shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(shape, dt, 1, fixed_point_position); + + // Create and configure function + NESoftmaxLayer smx_layer; + smx_layer.configure(&src, &dst); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + if(arm_compute::is_data_type_float(dt)) + { + std::uniform_real_distribution<> distribution(-10, 10); + library->fill(NEAccessor(src), distribution, 0); + } + else + { + int one_fixed = 1 << fixed_point_position; + std::uniform_int_distribution<> distribution(-one_fixed, one_fixed); + library->fill(NEAccessor(src), distribution, 0); + } + + // Compute function + smx_layer.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(SoftmaxLayer) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * CNNDataTypes(), shape, dt) +{ + // Set fixed point position data type allowed + int fixed_point_position = (arm_compute::is_data_type_fixed_point(dt)) ? 3 : 0; + + // Create tensors + Tensor src = create_tensor(shape, dt, 1, fixed_point_position); + Tensor dst = create_tensor(shape, dt, 1, fixed_point_position); + + BOOST_TEST(src.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NESoftmaxLayer smx_layer; + smx_layer.configure(&src, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + int step = 16 / arm_compute::data_size_from_type(dt); + const PaddingSize padding(0, required_padding(shape.x(), step), 0, 0); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * CNNFloatDataTypes(), shape, dt) +{ + // Compute function + Tensor dst = compute_softmax_layer(shape, dt); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_softmax_layer(shape, dt); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * CNNFloatDataTypes(), shape, dt) +{ + // Compute function + Tensor dst = compute_softmax_layer(shape, dt); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_softmax_layer(shape, dt); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(Quantized) +// Testing for fixed point position [1,6) as reciprocal limits the maximum fixed point position to 5 +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * CNNFixedPointDataTypes() * boost::unit_test::data::xrange(1, 6), + shape, dt, fixed_point_position) +{ + // Compute function + Tensor dst = compute_softmax_layer(shape, dt, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_softmax_layer(shape, dt, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_fixed_point); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * CNNFixedPointDataTypes() * boost::unit_test::data::xrange(1, 6), + shape, dt, fixed_point_position) +{ + // Compute function + Tensor dst = compute_softmax_layer(shape, dt, fixed_point_position); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_softmax_layer(shape, dt, fixed_point_position); + + // Validate output + validate(NEAccessor(dst), ref_dst, tolerance_fixed_point); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/NEON/Threshold.cpp b/tests/validation/NEON/Threshold.cpp new file mode 100644 index 0000000000..6ac6f3d26b --- /dev/null +++ b/tests/validation/NEON/Threshold.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Globals.h" +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TensorLibrary.h" +#include "TypePrinter.h" +#include "Utils.h" +#include "dataset/ThresholdDataset.h" +#include "validation/Datasets.h" +#include "validation/Reference.h" +#include "validation/Validation.h" + +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/NEON/functions/NEThreshold.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" + +#include "boost_wrapper.h" + +#include <random> +#include <string> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +/** Compute Threshold function. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] threshold Threshold. When the threshold type is RANGE, this is used as the lower threshold. + * @param[in] false_value value to set when the condition is not respected. + * @param[in] true_value value to set when the condition is respected. + * @param[in] type Thresholding type. Either RANGE or BINARY. + * @param[in] upper Upper threshold. Only used when the thresholding type is RANGE. + * + * @return Computed output tensor. + */ +Tensor compute_threshold(const TensorShape &shape, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + // Create and configure function + NEThreshold thrsh; + thrsh.configure(&src1, &dst, threshold, false_value, true_value, type, upper); + + // Allocate tensors + src1.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src1.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + library->fill_tensor_uniform(NEAccessor(src1), 0); + + // Compute function + thrsh.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(Threshold) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Configuration, + (SmallShapes() + LargeShapes()) * ThresholdDataset(), + shape, thrshConf) +{ + // Create tensors + Tensor src1 = create_tensor(shape, DataType::U8); + Tensor dst = create_tensor(shape, DataType::U8); + + BOOST_TEST(src1.info()->is_resizable()); + BOOST_TEST(dst.info()->is_resizable()); + + // Create and configure function + NEThreshold thrsh; + thrsh.configure(&src1, &dst, thrshConf.threshold, thrshConf.false_value, thrshConf.true_value, thrshConf.type, thrshConf.upper); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(src1.info()->valid_region(), valid_region); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding(0, required_padding(shape.x(), 16), 0, 0); + validate(src1.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, + SmallShapes() * ThresholdDataset(), + shape, thrshConf) +{ + // Compute function + Tensor dst = compute_threshold(shape, thrshConf.threshold, thrshConf.false_value, thrshConf.true_value, thrshConf.type, thrshConf.upper); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_threshold(shape, thrshConf.threshold, thrshConf.false_value, thrshConf.true_value, thrshConf.type, thrshConf.upper); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RunLarge, + LargeShapes() * ThresholdDataset(), + shape, thrshConf) +{ + // Compute function + Tensor dst = compute_threshold(shape, thrshConf.threshold, thrshConf.false_value, thrshConf.true_value, thrshConf.type, thrshConf.upper); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_threshold(shape, thrshConf.threshold, thrshConf.false_value, thrshConf.true_value, thrshConf.type, thrshConf.upper); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/Reference.cpp b/tests/validation/Reference.cpp new file mode 100644 index 0000000000..263c57b16b --- /dev/null +++ b/tests/validation/Reference.cpp @@ -0,0 +1,596 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Reference.h" + +#include "Globals.h" +#include "Helpers.h" +#include "ReferenceCPP.h" +#include "TensorLibrary.h" +#include "validation/Helpers.h" + +#include <random> + +using namespace arm_compute::test; + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +RawTensor Reference::compute_reference_integral_image(const TensorShape &shape) +{ + // Create reference + RawTensor ref_src = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::U32); + + // Fill reference + library->fill_tensor_uniform(ref_src, 0); + + // Compute reference + ReferenceCPP::integral_image(ref_src, ref_dst); + + return ref_dst; +} +RawTensor Reference::compute_reference_absolute_difference(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, dt_in0); + RawTensor ref_src2 = library->get(shape, dt_in1); + RawTensor ref_dst = library->get(shape, dt_out); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + library->fill_tensor_uniform(ref_src2, 1); + + // Compute reference + ReferenceCPP::absolute_difference(ref_src1, ref_src2, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_accumulate(const TensorShape &shape) +{ + // Create reference + RawTensor ref_src = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::S16); + + // Fill reference + library->fill_tensor_uniform(ref_src, 0); + library->fill_tensor_uniform(ref_dst, 1); + + // Compute reference + ReferenceCPP::accumulate(ref_src, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_accumulate_squared(const TensorShape &shape, uint32_t shift) +{ + // Create reference + RawTensor ref_src = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::S16); + + // Fill reference + // ref_dst tensor filled with non-negative values + library->fill_tensor_uniform(ref_src, 0); + library->fill_tensor_uniform(ref_dst, 1, static_cast<int16_t>(0), std::numeric_limits<int16_t>::max()); + + // Compute reference + ReferenceCPP::accumulate_squared(ref_src, ref_dst, shift); + + return ref_dst; +} + +RawTensor Reference::compute_reference_accumulate_weighted(const TensorShape &shape, float alpha) +{ + // Create reference + RawTensor ref_src = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::U8); + + // Fill reference + library->fill_tensor_uniform(ref_src, 0); + library->fill_tensor_uniform(ref_dst, 1); + + // Compute reference + ReferenceCPP::accumulate_weighted(ref_src, ref_dst, alpha); + + return ref_dst; +} + +RawTensor Reference::compute_reference_arithmetic_addition(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, ConvertPolicy convert_policy) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, dt_in0); + RawTensor ref_src2 = library->get(shape, dt_in1); + RawTensor ref_dst = library->get(shape, dt_out); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + library->fill_tensor_uniform(ref_src2, 1); + + // Compute reference + ReferenceCPP::arithmetic_addition(ref_src1, ref_src2, ref_dst, convert_policy); + + return ref_dst; +} + +RawTensor Reference::compute_reference_arithmetic_subtraction(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, ConvertPolicy convert_policy) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, dt_in0); + RawTensor ref_src2 = library->get(shape, dt_in1); + RawTensor ref_dst = library->get(shape, dt_out); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + library->fill_tensor_uniform(ref_src2, 1); + + // Compute reference + ReferenceCPP::arithmetic_subtraction(ref_src1, ref_src2, ref_dst, convert_policy); + + return ref_dst; +} + +RawTensor Reference::compute_reference_bitwise_and(const TensorShape &shape) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, DataType::U8); + RawTensor ref_src2 = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::U8); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + library->fill_tensor_uniform(ref_src2, 1); + + // Compute reference + ReferenceCPP::bitwise_and(ref_src1, ref_src2, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_bitwise_or(const TensorShape &shape) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, DataType::U8); + RawTensor ref_src2 = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::U8); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + library->fill_tensor_uniform(ref_src2, 1); + + // Compute reference + ReferenceCPP::bitwise_or(ref_src1, ref_src2, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_bitwise_xor(const TensorShape &shape) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, DataType::U8); + RawTensor ref_src2 = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::U8); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + library->fill_tensor_uniform(ref_src2, 1); + + // Compute reference + ReferenceCPP::bitwise_xor(ref_src1, ref_src2, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_bitwise_not(const TensorShape &shape) +{ + // Create reference + RawTensor ref_src = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::U8); + + // Fill reference + library->fill_tensor_uniform(ref_src, 0); + + // Compute reference + ReferenceCPP::bitwise_not(ref_src, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_box3x3(const TensorShape &shape) +{ + // Create reference + RawTensor ref_src = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::U8); + + // Fill reference + library->fill_tensor_uniform(ref_src, 0); + + // Compute reference + ReferenceCPP::box3x3(ref_src, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_depth_convert(const TensorShape &shape, DataType dt_in, DataType dt_out, ConvertPolicy policy, uint32_t shift, uint32_t fixed_point_position) +{ + RawTensor ref_src = library->get(shape, dt_in, 1, fixed_point_position); + RawTensor ref_dst = library->get(shape, dt_out, 1, fixed_point_position); + + // Fill reference + library->fill_tensor_uniform(ref_src, 0); + + // Compute reference + ReferenceCPP::depth_convert(ref_src, ref_dst, policy, shift); + + return ref_dst; +} + +RawTensor Reference::compute_reference_gemm(const TensorShape &src_shape1, const TensorShape &src_shape2, const TensorShape &src_shape3, + const TensorShape &dst_shape, float alpha, float beta, DataType dt, int fixed_point_position) +{ + RawTensor src1 = library->get(src_shape1, dt, 1, fixed_point_position); + RawTensor src2 = library->get(src_shape2, dt, 1, fixed_point_position); + RawTensor src3 = library->get(src_shape3, dt, 1, fixed_point_position); + RawTensor dst = library->get(dst_shape, dt, 1, fixed_point_position); + + // Fill reference + if(dt == DataType::F32) + { + std::uniform_real_distribution<> distribution(-1.0f, 1.0f); + library->fill(src1, distribution, 0); + library->fill(src2, distribution, 1); + library->fill(src3, distribution, 2); + } + else + { + library->fill_tensor_uniform(src1, 0); + library->fill_tensor_uniform(src2, 1); + library->fill_tensor_uniform(src3, 2); + } + + // Compute reference + ReferenceCPP::gemm(src1, src2, src3, dst, alpha, beta); + + return dst; +} + +RawTensor Reference::compute_reference_pixel_wise_multiplication(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, float scale, ConvertPolicy convert_policy, + RoundingPolicy rounding_policy) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, dt_in0); + RawTensor ref_src2 = library->get(shape, dt_in1); + RawTensor ref_dst = library->get(shape, dt_out); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + library->fill_tensor_uniform(ref_src2, 1); + + // Compute reference + ReferenceCPP::pixel_wise_multiplication(ref_src1, ref_src2, ref_dst, scale, convert_policy, rounding_policy); + + return ref_dst; +} + +RawTensor Reference::compute_reference_fixed_point_pixel_wise_multiplication(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, float scale, int fixed_point_position, + ConvertPolicy convert_policy, RoundingPolicy rounding_policy) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, dt_in0, 1, fixed_point_position); + RawTensor ref_src2 = library->get(shape, dt_in1, 1, fixed_point_position); + RawTensor ref_dst = library->get(shape, dt_out, 1, fixed_point_position); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + library->fill_tensor_uniform(ref_src2, 1); + + // Compute reference + ReferenceCPP::fixed_point_pixel_wise_multiplication(ref_src1, ref_src2, ref_dst, scale, convert_policy, rounding_policy); + + return ref_dst; +} + +RawTensor Reference::compute_reference_threshold(const TensorShape &shape, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper) +{ + // Create reference + RawTensor ref_src1 = library->get(shape, DataType::U8); + RawTensor ref_dst = library->get(shape, DataType::U8); + + // Fill reference + library->fill_tensor_uniform(ref_src1, 0); + + // Compute reference + ReferenceCPP::threshold(ref_src1, ref_dst, threshold, false_value, true_value, type, upper); + + return ref_dst; +} + +RawTensor Reference::compute_reference_activation_layer(const TensorShape &shape, DataType dt, ActivationLayerInfo act_info, int fixed_point_position) +{ + // Create reference + RawTensor ref_src = library->get(shape, dt, 1, fixed_point_position); + RawTensor ref_dst = library->get(shape, dt, 1, fixed_point_position); + + // Fill reference + if(dt == DataType::F32) + { + float min_bound = 0; + float max_bound = 0; + std::tie(min_bound, max_bound) = get_activation_layer_test_bounds<float>(act_info.activation()); + std::uniform_real_distribution<> distribution(min_bound, max_bound); + library->fill(ref_src, distribution, 0); + } + else + { + int min_bound = 0; + int max_bound = 0; + std::tie(min_bound, max_bound) = get_activation_layer_test_bounds<int8_t>(act_info.activation(), fixed_point_position); + std::uniform_int_distribution<> distribution(min_bound, max_bound); + library->fill(ref_src, distribution, 0); + } + + // Compute reference + ReferenceCPP::activation_layer(ref_src, ref_dst, act_info); + + return ref_dst; +} + +RawTensor Reference::compute_reference_batch_normalization_layer(const TensorShape &shape0, const TensorShape &shape1, DataType dt, float epsilon, int fixed_point_position) +{ + // Create reference + RawTensor ref_src = library->get(shape0, dt, 1, fixed_point_position); + RawTensor ref_dst = library->get(shape0, dt, 1, fixed_point_position); + RawTensor ref_mean = library->get(shape1, dt, 1, fixed_point_position); + RawTensor ref_var = library->get(shape1, dt, 1, fixed_point_position); + RawTensor ref_beta = library->get(shape1, dt, 1, fixed_point_position); + RawTensor ref_gamma = library->get(shape1, dt, 1, fixed_point_position); + + // Fill tensors with values from -1 to 1. + if(dt == DataType::F32) + { + float min_bound = 0.f; + float max_bound = 0.f; + std::tie(min_bound, max_bound) = get_batchnormalization_layer_test_bounds<float>(); + std::uniform_real_distribution<> distribution(min_bound, max_bound); + std::uniform_real_distribution<> distribution_var(0, max_bound); + library->fill(ref_src, distribution, 0); + library->fill(ref_mean, distribution, 1); + library->fill(ref_var, distribution_var, 0); + library->fill(ref_beta, distribution, 3); + library->fill(ref_gamma, distribution, 4); + } + else + { + int min_bound = 0; + int max_bound = 0; + std::tie(min_bound, max_bound) = get_batchnormalization_layer_test_bounds<int8_t>(fixed_point_position); + std::uniform_int_distribution<> distribution(min_bound, max_bound); + std::uniform_int_distribution<> distribution_var(0, max_bound); + library->fill(ref_src, distribution, 0); + library->fill(ref_mean, distribution, 1); + library->fill(ref_var, distribution_var, 0); + library->fill(ref_beta, distribution, 3); + library->fill(ref_gamma, distribution, 4); + } + + // Compute reference + ReferenceCPP::batch_normalization_layer(ref_src, ref_dst, ref_mean, ref_var, ref_beta, ref_gamma, epsilon, fixed_point_position); + + return ref_dst; +} + +RawTensor Reference::compute_reference_convolution_layer(const TensorShape &input_shape, const TensorShape &weights_shape, const TensorShape &bias_shape, const TensorShape &output_shape, DataType dt, + const PadStrideInfo &conv_info, int fixed_point_position) +{ + // Create reference + RawTensor ref_src = library->get(input_shape, dt, 1, fixed_point_position); + RawTensor ref_weights = library->get(weights_shape, dt, 1, fixed_point_position); + RawTensor ref_bias = library->get(bias_shape, dt, 1, fixed_point_position); + RawTensor ref_dst = library->get(output_shape, dt, 1, fixed_point_position); + + // Fill reference + if(dt == DataType::F32) + { + std::uniform_real_distribution<> distribution(-1.0f, 1.0f); + library->fill(ref_src, distribution, 0); + library->fill(ref_weights, distribution, 1); + library->fill(ref_bias, distribution, 2); + } + else + { + library->fill_tensor_uniform(ref_src, 0); + library->fill_tensor_uniform(ref_weights, 1); + library->fill_tensor_uniform(ref_bias, 2); + } + + // Compute reference + ReferenceCPP::convolution_layer(ref_src, ref_weights, ref_bias, ref_dst, conv_info); + + return ref_dst; +} + +RawTensor Reference::compute_reference_fully_connected_layer(const TensorShape &input_shape, const TensorShape &weights_shape, const TensorShape &bias_shape, const TensorShape &output_shape, + DataType dt, bool transpose_weights, int fixed_point_position) +{ + // Create reference + RawTensor ref_src = library->get(input_shape, dt, 1, fixed_point_position); + RawTensor ref_bias = library->get(bias_shape, dt, 1, fixed_point_position); + RawTensor ref_dst = library->get(output_shape, dt, 1, fixed_point_position); + + // Swap the first and second dimension of weights' shape if transpose_weights is true + TensorShape ws = weights_shape; + if(transpose_weights) + { + const size_t dimx = ws.x(); + ws.set(0, ws.y()); + ws.set(1, dimx); + } + + RawTensor ref_weights = library->get(ws, dt, 1, fixed_point_position); + + // Fill reference + if(dt == DataType::F32) + { + std::uniform_real_distribution<> distribution(-1.0f, 1.0f); + library->fill(ref_src, distribution, 0); + library->fill(ref_weights, distribution, 1); + library->fill(ref_bias, distribution, 2); + } + else + { + library->fill_tensor_uniform(ref_src, 0); + library->fill_tensor_uniform(ref_weights, 1); + library->fill_tensor_uniform(ref_bias, 2); + } + + // Compute reference + ReferenceCPP::fully_connected_layer(ref_src, ref_weights, ref_bias, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_normalization_layer(const TensorShape &shape, DataType dt, NormalizationLayerInfo norm_info, int fixed_point_position) +{ + // Create reference + RawTensor ref_src = library->get(shape, dt, 1, fixed_point_position); + RawTensor ref_dst = library->get(shape, dt, 1, fixed_point_position); + + // Fill reference + if(dt == DataType::QS8) + { + const int8_t one_fixed_point = 1 << fixed_point_position; + const int8_t minus_one_fixed_point = -one_fixed_point; + library->fill_tensor_uniform(ref_src, 0, minus_one_fixed_point, one_fixed_point); + } + else + { + library->fill_tensor_uniform(ref_src, 0); + } + + // Compute reference + ReferenceCPP::normalization_layer(ref_src, ref_dst, norm_info); + + return ref_dst; +} + +RawTensor Reference::compute_reference_pooling_layer(const TensorShape &shape_in, const TensorShape &shape_out, DataType dt, PoolingLayerInfo pool_info, int fixed_point_position) +{ + // Create reference + RawTensor ref_src = library->get(shape_in, dt, 1, fixed_point_position); + RawTensor ref_dst = library->get(shape_out, dt, 1, fixed_point_position); + + // Fill reference + int min = 0; + int max = 0; + switch(dt) + { + case DataType::F32: + min = -1; + max = 1; + break; + case DataType::QS8: + min = -(1 << fixed_point_position); + max = (1 << fixed_point_position); + break; + default: + ARM_COMPUTE_ERROR("DataType not supported."); + } + std::uniform_real_distribution<> distribution(min, max); + library->fill(ref_src, distribution, 0.0); + + // Compute reference + ReferenceCPP::pooling_layer(ref_src, ref_dst, pool_info, fixed_point_position); + + return ref_dst; +} + +RawTensor Reference::compute_reference_softmax_layer(const TensorShape &shape, DataType dt, int fixed_point_position) +{ + // Create reference + RawTensor ref_src = library->get(shape, dt, 1, fixed_point_position); + RawTensor ref_dst = library->get(shape, dt, 1, fixed_point_position); + + // Fill reference + if(arm_compute::is_data_type_float(dt)) + { + std::uniform_real_distribution<> distribution(-10, 10); + library->fill(ref_src, distribution, 0); + } + else + { + int one_fixed = 1 << fixed_point_position; + std::uniform_int_distribution<> distribution(-one_fixed, one_fixed); + library->fill(ref_src, distribution, 0); + } + + // Compute reference + ReferenceCPP::softmax_layer(ref_src, ref_dst); + + return ref_dst; +} + +RawTensor Reference::compute_reference_fixed_point_operation(const TensorShape &shape, DataType dt_in, DataType dt_out, FixedPointOp op, int fixed_point_position) +{ + // Create reference + RawTensor ref_src = library->get(shape, dt_in, 1, fixed_point_position); + RawTensor ref_dst = library->get(shape, dt_out, 1, fixed_point_position); + + // Fill reference + int min = 0; + int max = 0; + switch(op) + { + case(FixedPointOp::INV_SQRT): + min = 32; + max = 127; + break; + case(FixedPointOp::LOG): + min = (1 << (fixed_point_position - 1)); + max = 63; + break; + case(FixedPointOp::EXP): + min = 1; + max = (1 << (fixed_point_position - 1)); + break; + case(FixedPointOp::RECIPROCAL): + min = 15; + max = 100; + break; + default: + ARM_COMPUTE_ERROR("Fixed point operation not supported"); + } + std::uniform_int_distribution<> distribution(min, max); + library->fill(ref_src, distribution, 0); + + // Compute reference + ReferenceCPP::fixed_point_operation(ref_src, ref_dst, op); + + return ref_dst; +} + +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/Reference.h b/tests/validation/Reference.h new file mode 100644 index 0000000000..4e5b462f9e --- /dev/null +++ b/tests/validation/Reference.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_REFERENCE_REFERENCE_H__ +#define __ARM_COMPUTE_TEST_REFERENCE_REFERENCE_H__ + +#include "RawTensor.h" +#include "Types.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +/** Interface for reference implementations. */ +class Reference +{ +public: + /** Compute reference integral image. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_integral_image(const TensorShape &shape); + /** Compute reference absolute difference. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_absolute_difference(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out); + /** Compute reference accumulate. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_accumulate(const TensorShape &shape); + /** Compute reference accumulate. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] shift A uint32_t value within the range of [0, 15] + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_accumulate_squared(const TensorShape &shape, uint32_t shift); + /** Compute reference accumulate. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] alpha A float value within the range of [0, 1] + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_accumulate_weighted(const TensorShape &shape, float alpha); + /** Compute reference arithmetic addition. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] convert_policy Overflow policy of the operation. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_arithmetic_addition(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, ConvertPolicy convert_policy); + /** Compute reference arithmetic subtraction. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] convert_policy Overflow policy of the operation. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_arithmetic_subtraction(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, ConvertPolicy convert_policy); + /** Compute reference bitwise and. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_bitwise_and(const TensorShape &shape); + /** Compute reference bitwise or. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_bitwise_or(const TensorShape &shape); + /** Compute reference bitwise xor. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_bitwise_xor(const TensorShape &shape); + /** Compute reference bitwise not. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_bitwise_not(const TensorShape &shape); + /** Compute reference 3-by-3 box filter. + * + * @param[in] shape Shape of the input and output tensors. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_box3x3(const TensorShape &shape); + /** Compute reference depth convert. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in Data type of input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] policy Overflow policy of the operation. + * @param[in] shift Value for down/up conversions. Must be 0 <= shift < 8. + * @param[in] fixed_point_position Fixed point position. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_depth_convert(const TensorShape &shape, DataType dt_in, DataType dt_out, ConvertPolicy policy, uint32_t shift, uint32_t fixed_point_position); + /** Compute matrix multiply function. + * + * @param[in] src_shape1 First input tensor shape + * @param[in] src_shape2 Second input tensor shape + * @param[in] src_shape3 Third input tensor shape + * @param[out] dst_shape Output tensor. + * @param[in] alpha Weight of the matrix product + * @param[in] beta Weight of the third matrix + * @param[in] dt Tensor's data type + * @param[in] fixed_point_position (Optional) Number of bits for the fractional part of the fixed point numbers + * + * @return Computed output tensor. + */ + static RawTensor compute_reference_gemm(const TensorShape &src_shape1, const TensorShape &src_shape2, const TensorShape &src_shape3, + const TensorShape &dst_shape, float alpha, float beta, DataType dt, int fixed_point_position = 0); + /** Compute reference pixel-wise multiplication + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] scale Non-negative scale. + * @param[in] convert_policy Overflow policy of the operation. + * @param[in] rounding_policy Rounding policy of the operation. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_pixel_wise_multiplication(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, float scale, ConvertPolicy convert_policy, + RoundingPolicy rounding_policy); + /** Compute reference pixel-wise multiplication. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in0 Data type of first input tensor. + * @param[in] dt_in1 Data type of second input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] scale Scale to apply after multiplication. Must be positive. + * @param[in] fixed_point_position Fixed point position that expresses the number of bits for the fractional part of the number. + * @param[in] convert_policy Overflow policy of the operation. + * @param[in] rounding_policy Rounding policy of the operation. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_fixed_point_pixel_wise_multiplication(const TensorShape &shape, DataType dt_in0, DataType dt_in1, DataType dt_out, float scale, int fixed_point_position, + ConvertPolicy convert_policy, RoundingPolicy rounding_policy); + /** Compute reference threshold. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] threshold Threshold. When the threshold type is RANGE, this is used as the lower threshold. + * @param[in] false_value value to set when the condition is not respected. + * @param[in] true_value value to set when the condition is respected. + * @param[in] type Thresholding type. Either RANGE or BINARY. + * @param[in] upper Upper threshold. Only used when the thresholding type is RANGE. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_threshold(const TensorShape &shape, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper); + /** Compute reference activation layer. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt Data type of the tensors. + * @param[in] act_info Activation layer information. + * @param[in] fixed_point_position (Optional)Number of bits for the fractional part of fixed point numbers. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_activation_layer(const TensorShape &shape, DataType dt, ActivationLayerInfo act_info, int fixed_point_position = 0); + /** Compute reference batch normalization layer. + * + * @param[in] shape0 Shape of the input and output tensors. + * @param[in] shape1 Shape of the vector tensors. + * @param[in] dt Data type of all input and output tensors. + * @param[in] epsilon Small value to avoid division with zero. + * @param[in] fixed_point_position Fixed point position. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_batch_normalization_layer(const TensorShape &shape0, const TensorShape &shape1, DataType dt, float epsilon, int fixed_point_position = 0); + /** Compute reference pixel-wise multiplication + * + * @param[in] input_shape Shape for the input tensor + * @param[in] weights_shape Shape for the weights tensor + * @param[in] bias_shape Shape for the bias tensor + * @param[in] output_shape Shape for the output tensor + * @param[in] dt Data type to use + * @param[in] conv_info Pads and strides information for the convolution layer + * @param[in] fixed_point_position Number of bits for the fractional part of the fixed point numbers + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_convolution_layer(const TensorShape &input_shape, const TensorShape &weights_shape, const TensorShape &bias_shape, const TensorShape &output_shape, DataType dt, + const PadStrideInfo &conv_info, int fixed_point_position); + /** Compute reference for fully connected layer function + * + * @param[in] input_shape Shape for the input tensor + * @param[in] weights_shape Shape for the weights tensor + * @param[in] bias_shape Shape for the bias tensor + * @param[in] output_shape Shape for the output tensor + * @param[in] dt Data type to use + * @param[in] transpose_weights Transpose the weights if true + * @param[in] fixed_point_position Number of bits for the fractional part of the fixed point numbers + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_fully_connected_layer(const TensorShape &input_shape, const TensorShape &weights_shape, const TensorShape &bias_shape, const TensorShape &output_shape, DataType dt, + bool transpose_weights, int fixed_point_position); + /** Compute reference normalization layer. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt Data type of input and output tensors. + * @param[in] norm_info Normalization Layer information. + * @param[in] fixed_point_position (Optional) Fixed point position that expresses the number of bits for the fractional part of the number when the tensor's data type is QS8 or QS16 (default = 0). + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_normalization_layer(const TensorShape &shape, DataType dt, NormalizationLayerInfo norm_info, int fixed_point_position = 0); + /** Compute reference pooling layer. + * + * @param[in] shape_in Shape of the input tensor. + * @param[in] shape_out Shape of the output tensor. + * @param[in] dt Data type of input and output tensors. + * @param[in] pool_info Pooling Layer information. + * @param[in] fixed_point_position (Optional) Number of bits for the fractional part of the fixed point numbers. + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_pooling_layer(const TensorShape &shape_in, const TensorShape &shape_out, DataType dt, PoolingLayerInfo pool_info, int fixed_point_position = 0); + /** Compute reference softmax layer. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt Data type of input and output tensors. + * @param[in] fixed_point_position (Optional) Number of bits for the fractional part of the fixed point numbers + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_softmax_layer(const TensorShape &shape, DataType dt, int fixed_point_position = 0); + /** Compute reference fixed point operation. + * + * @param[in] shape Shape of the input and output tensors. + * @param[in] dt_in Data type of the input tensor. + * @param[in] dt_out Data type of the output tensor. + * @param[in] op Fixed point operation to perform. + * @param[in] fixed_point_position Number of bits for the fractional part of the fixed point numbers + * + * @return Computed raw tensor. + */ + static RawTensor compute_reference_fixed_point_operation(const TensorShape &shape, DataType dt_in, DataType dt_out, FixedPointOp op, int fixed_point_position); + +protected: + Reference() = default; + ~Reference() = default; +}; +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif diff --git a/tests/validation/ReferenceCPP.cpp b/tests/validation/ReferenceCPP.cpp new file mode 100644 index 0000000000..ddb84835c3 --- /dev/null +++ b/tests/validation/ReferenceCPP.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "ReferenceCPP.h" + +#include "TensorFactory.h" +#include "TensorOperations.h" +#include "TensorVisitors.h" +#include "TypePrinter.h" + +#include "arm_compute/core/Coordinates.h" +#include "arm_compute/core/Error.h" +#include "arm_compute/core/TensorInfo.h" +#include "arm_compute/core/TensorShape.h" +#include "arm_compute/runtime/Tensor.h" + +#include "boost_wrapper.h" + +#include <functional> +#include <numeric> +#include <vector> + +using namespace arm_compute::test::validation::tensor_visitors; + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +// Absolute difference +void ReferenceCPP::absolute_difference(const RawTensor &src1, const RawTensor &src2, RawTensor &dst) +{ + const TensorVariant s1 = TensorFactory::get_tensor(src1); + const TensorVariant s2 = TensorFactory::get_tensor(src2); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(absolute_difference_visitor(), s1, s2, d); +} +// Integral image +void ReferenceCPP::integral_image(const RawTensor &src, RawTensor &dst) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst.data_type() != DataType::U32); + const Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<uint32_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<uint32_t *>(dst.data())); + tensor_operations::integral_image(s, d); +} +// Accumulate +void ReferenceCPP::accumulate(const RawTensor &src, RawTensor &dst) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst.data_type() != DataType::S16); + const Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<int16_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<int16_t *>(dst.data())); + tensor_operations::accumulate(s, d); +} + +// Accumulate squared +void ReferenceCPP::accumulate_squared(const RawTensor &src, RawTensor &dst, uint32_t shift) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst.data_type() != DataType::S16); + const Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<int16_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<int16_t *>(dst.data())); + tensor_operations::accumulate_squared(s, d, shift); +} + +// Accumulate weighted +void ReferenceCPP::accumulate_weighted(const RawTensor &src, RawTensor &dst, float alpha) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst.data_type() != DataType::U8); + const Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<uint8_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<uint8_t *>(dst.data())); + tensor_operations::accumulate_weighted(s, d, alpha); +} + +// Arithmetic addition +void ReferenceCPP::arithmetic_addition(const RawTensor &src1, const RawTensor &src2, RawTensor &dst, ConvertPolicy convert_policy) +{ + const TensorVariant s1 = TensorFactory::get_tensor(src1); + const TensorVariant s2 = TensorFactory::get_tensor(src2); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(arithmetic_addition_visitor(convert_policy), s1, s2, d); +} + +// Arithmetic subtraction +void ReferenceCPP::arithmetic_subtraction(const RawTensor &src1, const RawTensor &src2, RawTensor &dst, ConvertPolicy convert_policy) +{ + const TensorVariant s1 = TensorFactory::get_tensor(src1); + const TensorVariant s2 = TensorFactory::get_tensor(src2); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(arithmetic_subtraction_visitor(convert_policy), s1, s2, d); +} + +// Bitwise and +void ReferenceCPP::bitwise_and(const RawTensor &src1, const RawTensor &src2, RawTensor &dst) +{ + ARM_COMPUTE_ERROR_ON(src1.data_type() != DataType::U8 || src2.data_type() != DataType::U8 || dst.data_type() != DataType::U8); + const Tensor<uint8_t> s1(src1.shape(), src1.data_type(), src1.fixed_point_position(), reinterpret_cast<const uint8_t *>(src1.data())); + const Tensor<uint8_t> s2(src2.shape(), src2.data_type(), src2.fixed_point_position(), reinterpret_cast<const uint8_t *>(src2.data())); + Tensor<uint8_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<uint8_t *>(dst.data())); + tensor_operations::bitwise_and(s1, s2, d); +} + +// Bitwise or +void ReferenceCPP::bitwise_or(const RawTensor &src1, const RawTensor &src2, RawTensor &dst) +{ + ARM_COMPUTE_ERROR_ON(src1.data_type() != DataType::U8 || src2.data_type() != DataType::U8 || dst.data_type() != DataType::U8); + const Tensor<uint8_t> s1(src1.shape(), src1.data_type(), src1.fixed_point_position(), reinterpret_cast<const uint8_t *>(src1.data())); + const Tensor<uint8_t> s2(src2.shape(), src2.data_type(), src2.fixed_point_position(), reinterpret_cast<const uint8_t *>(src2.data())); + Tensor<uint8_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<uint8_t *>(dst.data())); + tensor_operations::bitwise_or(s1, s2, d); +} + +// Bitwise xor +void ReferenceCPP::bitwise_xor(const RawTensor &src1, const RawTensor &src2, RawTensor &dst) +{ + ARM_COMPUTE_ERROR_ON(src1.data_type() != DataType::U8 || src2.data_type() != DataType::U8 || dst.data_type() != DataType::U8); + const Tensor<uint8_t> s1(src1.shape(), src1.data_type(), src1.fixed_point_position(), reinterpret_cast<const uint8_t *>(src1.data())); + const Tensor<uint8_t> s2(src2.shape(), src2.data_type(), src2.fixed_point_position(), reinterpret_cast<const uint8_t *>(src2.data())); + Tensor<uint8_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<uint8_t *>(dst.data())); + tensor_operations::bitwise_xor(s1, s2, d); +} + +// Bitwise not +void ReferenceCPP::bitwise_not(const RawTensor &src, RawTensor &dst) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst.data_type() != DataType::U8); + const Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<uint8_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<uint8_t *>(dst.data())); + tensor_operations::bitwise_not(s, d); +} + +// 3-by-3 box filter +void ReferenceCPP::box3x3(const RawTensor &src, RawTensor &dst) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst.data_type() != DataType::U8); + const Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<uint8_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<uint8_t *>(dst.data())); + tensor_operations::box3x3(s, d); +} + +// Depth conversion +void ReferenceCPP::depth_convert(const RawTensor &src, RawTensor &dst, ConvertPolicy policy, uint32_t shift) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::depth_convert_visitor(policy, shift), s, d); +} + +// GEMM +void ReferenceCPP::gemm(const RawTensor &src1, const RawTensor &src2, const RawTensor &src3, + RawTensor &dst, float alpha, float beta) +{ + const TensorVariant s1 = TensorFactory::get_tensor(src1); + const TensorVariant s2 = TensorFactory::get_tensor(src2); + const TensorVariant s3 = TensorFactory::get_tensor(src3); + TensorVariant d = TensorFactory::get_tensor(dst); + + boost::apply_visitor(tensor_visitors::gemm_visitor(s1, s2, s3, alpha, beta), d); +} + +// Pixel-wise multiplication +void ReferenceCPP::pixel_wise_multiplication(const RawTensor &src1, const RawTensor &src2, RawTensor &dst, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) +{ + const TensorVariant s1 = TensorFactory::get_tensor(src1); + const TensorVariant s2 = TensorFactory::get_tensor(src2); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(pixel_wise_multiplication_visitor(scale, convert_policy, rounding_policy), s1, s2, d); +} + +// Fixed-point Pixel-wise multiplication +void ReferenceCPP::fixed_point_pixel_wise_multiplication(const RawTensor &src1, const RawTensor &src2, RawTensor &dst, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) +{ + const TensorVariant s1 = TensorFactory::get_tensor(src1); + const TensorVariant s2 = TensorFactory::get_tensor(src2); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::fixed_point_pixel_wise_multiplication_visitor(s1, s2, scale, convert_policy, rounding_policy), d); +} + +// Threshold +void ReferenceCPP::threshold(const RawTensor &src, RawTensor &dst, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper) +{ + ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst.data_type() != DataType::U8); + const Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data())); + Tensor<uint8_t> d(dst.shape(), dst.data_type(), dst.fixed_point_position(), reinterpret_cast<uint8_t *>(dst.data())); + threshold_operation(s, d, threshold, false_value, true_value, type, upper); +} + +// Activation layer +void ReferenceCPP::activation_layer(const RawTensor &input, RawTensor &output, ActivationLayerInfo act_info) +{ + const TensorVariant s = TensorFactory::get_tensor(input); + TensorVariant d = TensorFactory::get_tensor(output); + boost::apply_visitor(tensor_visitors::activation_layer_visitor(s, act_info), d); +} + +// Batch Normalization Layer +void ReferenceCPP::batch_normalization_layer(const RawTensor &src, RawTensor &dst, const RawTensor &mean, const RawTensor &var, const RawTensor &beta, const RawTensor &gamma, float epsilon, + int fixed_point_position) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + TensorVariant d = TensorFactory::get_tensor(dst); + const TensorVariant m = TensorFactory::get_tensor(mean); + const TensorVariant v = TensorFactory::get_tensor(var); + const TensorVariant b = TensorFactory::get_tensor(beta); + const TensorVariant g = TensorFactory::get_tensor(gamma); + boost::apply_visitor(tensor_visitors::batch_normalization_layer_visitor(s, m, v, b, g, epsilon, fixed_point_position), d); +} + +// Convolution Layer +void ReferenceCPP::convolution_layer(const RawTensor &src, const RawTensor &weights, const RawTensor &bias, RawTensor &dst, const PadStrideInfo &conv_info) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + const TensorVariant w = TensorFactory::get_tensor(weights); + const TensorVariant b = TensorFactory::get_tensor(bias); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::convolution_layer_visitor(s, w, b, conv_info), d); +} + +// Fully connected layer +void ReferenceCPP::fully_connected_layer(const RawTensor &src, const RawTensor &weights, const RawTensor &bias, RawTensor &dst) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + const TensorVariant w = TensorFactory::get_tensor(weights); + const TensorVariant b = TensorFactory::get_tensor(bias); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::fully_connected_layer_visitor(s, w, b), d); +} + +// Normalization Layer +void ReferenceCPP::normalization_layer(const RawTensor &src, RawTensor &dst, NormalizationLayerInfo norm_info) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::normalization_layer_visitor(s, norm_info), d); +} + +// Pooling Layer +void ReferenceCPP::pooling_layer(const RawTensor &src, RawTensor &dst, PoolingLayerInfo pool_info, int fixed_point_position) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::pooling_layer_visitor(s, pool_info, fixed_point_position), d); +} + +// Softmax Layer +void ReferenceCPP::softmax_layer(const RawTensor &src, RawTensor &dst) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::softmax_layer_visitor(s), d); +} + +// Fixed point operation +void ReferenceCPP::fixed_point_operation(const RawTensor &src, RawTensor &dst, FixedPointOp op) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::fixed_point_operation_visitor(s, op), d); +} + +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/ReferenceCPP.h b/tests/validation/ReferenceCPP.h new file mode 100644 index 0000000000..be5a733896 --- /dev/null +++ b/tests/validation/ReferenceCPP.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_REFERENCE_REFERENCE_CPP_H__ +#define __ARM_COMPUTE_TEST_REFERENCE_REFERENCE_CPP_H__ + +#include "Reference.h" + +#include "RawTensor.h" + +#include <ostream> + +namespace arm_compute +{ +class Tensor; + +namespace test +{ +namespace validation +{ +/** C++ reference implementation. */ +class ReferenceCPP final : public Reference +{ +public: + /** Function to compute the integral image of a tensor. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + */ + static void integral_image(const RawTensor &src, RawTensor &dst); + /** Function to compute the absolute difference between two tensors. + * + * @param[in] src1 First tensor. + * @param[in] src2 Second tensor. + * @param[out] dst Result tensor. + */ + static void absolute_difference(const RawTensor &src1, const RawTensor &src2, RawTensor &dst); + /** Function to accumulate an input tensor into an output tensor. + * + * @param[in] src Input tensor. + * @param[in, out] dst Result tensor. + */ + static void accumulate(const RawTensor &src, RawTensor &dst); + /** Function to accumulate a squared value from an input tensor to an output tensor. + * + * @param[in] src Input tensor. + * @param[in, out] dst Result tensor. + * @param[in] shift A uint32_t value within the range of [0, 15] + */ + static void accumulate_squared(const RawTensor &src, RawTensor &dst, uint32_t shift); + /** Function to accumulate a weighted value from an input tensor to an output tensor. + * + * @param[in] src Input tensor. + * @param[in, out] dst Result tensor. + * @param[in] alpha A float value within the range of [0, 1] + */ + static void accumulate_weighted(const RawTensor &src, RawTensor &dst, float alpha); + /** Arithmetic addition of @p src1 and @p src2 + * + * @param[in] src1 First tensor. + * @param[in] src2 Second tensor. + * @param[out] dst Result tensor. + * @param[in] convert_policy Overflow policy. + */ + static void arithmetic_addition(const RawTensor &src1, const RawTensor &src2, RawTensor &dst, ConvertPolicy convert_policy); + /** Arithmetic subtraction of @p src2 from @p src1 + * + * @param[in] src1 First tensor. + * @param[in] src2 Second tensor. + * @param[out] dst Result tensor. + * @param[in] convert_policy Overflow policy. + */ + static void arithmetic_subtraction(const RawTensor &src1, const RawTensor &src2, RawTensor &dst, ConvertPolicy convert_policy); + /** Function to compute the bitwise and between two tensors. + * + * @param[in] src1 First tensor. + * @param[in] src2 Second tensor. + * @param[out] dst Result tensor. + */ + static void bitwise_and(const RawTensor &src1, const RawTensor &src2, RawTensor &dst); + /** Function to compute the bitwise or between two tensors. + * + * @param[in] src1 First tensor. + * @param[in] src2 Second tensor. + * @param[out] dst Result tensor. + */ + static void bitwise_or(const RawTensor &src1, const RawTensor &src2, RawTensor &dst); + /** Function to compute the bitwise xor between two tensors. + * + * @param[in] src1 First tensor. + * @param[in] src2 Second tensor. + * @param[out] dst Result tensor. + */ + static void bitwise_xor(const RawTensor &src1, const RawTensor &src2, RawTensor &dst); + /** Function to compute the bitwise not of a tensor. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + */ + static void bitwise_not(const RawTensor &src, RawTensor &dst); + /** Function to compute 3-by-3 box filtered result tensor. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + */ + static void box3x3(const RawTensor &src, RawTensor &dst); + /** Depth conversion from @p src to @p dst + * + * @param[in] src First tensor. + * @param[out] dst Result tensor. + * @param[in] policy Overflow policy. + * @param[in] shift Value for down/up conversions. + */ + static void depth_convert(const RawTensor &src, RawTensor &dst, ConvertPolicy policy, uint32_t shift); + /** Compute GEMM function. + * + * @param[in] src1 First input tensor + * @param[in] src2 Second input tensor + * @param[in] src3 Third input tensor + * @param[out] dst Output tensr + * @param[in] alpha Weight of the matrix product + * @param[in] beta Weight of the third matrix + */ + static void gemm(const RawTensor &src1, const RawTensor &src2, const RawTensor &src3, + RawTensor &dst, float alpha, float beta); + /** Element-wise multiplication of @p src1, @p src2 and @p scale + * + * @param[in] src1 First tensor. + * @param[in] src2 Second tensor. + * @param[out] dst Result tensor. + * @param[in] scale A non-negative float multiplied to each product. + * @param[in] convert_policy Overflow policy. + * @param[in] rounding_policy Rounding policy. + */ + static void pixel_wise_multiplication(const RawTensor &src1, const RawTensor &src2, RawTensor &dst, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy); + /** Fixed-point Pixel-wise multiplication of @p src1 by @p src2 + * + * @param[in] src1 First tensor. + * @param[in] src2 Second tensor. + * @param[out] dst Result tensor. + * @param[in] scale A non-negative float multiplied to each product. + * @param[in] convert_policy Overflow policy. + * @param[in] rounding_policy Rounding policy. + */ + static void fixed_point_pixel_wise_multiplication(const RawTensor &src1, const RawTensor &src2, RawTensor &dst, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy); + /** Threshold of@p src to @p dst + * + * @param[in] src First tensor. + * @param[out] dst Result tensor. + * @param[in] threshold Threshold. When the threhold type is RANGE, this is used as the lower threshold. + * @param[in] false_value value to set when the condition is not respected. + * @param[in] true_value value to set when the condition is respected. + * @param[in] type Thresholding type. Either RANGE or BINARY. + * @param[in] upper Upper threshold. Only used when the thresholding type is RANGE. + */ + static void threshold(const RawTensor &src, RawTensor &dst, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper); + /** Activation layer of @p src base on information from @p act_info. + * + * @param[in] input Input tensor. + * @param[in] output Second tensor. + * @param[out] act_info Activation layer information. + */ + static void activation_layer(const RawTensor &input, RawTensor &output, ActivationLayerInfo act_info); + /** Batch Normalization of @p src based on the information from @p norm_info. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + * @param[out] mean Mean vector tensor. + * @param[out] var Var vector tensor. + * @param[out] beta Beta vector tensor. + * @param[out] gamma Gamma vector tensor. + * @param[in] epsilon Small value to avoid division with zero. + * @param[in] fixed_point_position Fixed point position. + */ + static void batch_normalization_layer(const RawTensor &src, RawTensor &dst, const RawTensor &mean, const RawTensor &var, const RawTensor &beta, const RawTensor &gamma, float epsilon, + int fixed_point_position = 0); + /** Convolution layer function + * + * @param[in] src Input tensor. + * @param[in] weights Weights tensor. + * @param[in] bias Bias tensor. + * @param[out] dst Result tensor. + * @param[in] conv_info Pads and strides information for the convolution layer. + */ + static void convolution_layer(const RawTensor &src, const RawTensor &weights, const RawTensor &bias, RawTensor &dst, const PadStrideInfo &conv_info); + /** Fully connected layer function + * + * @param[in] src Input tensor + * @param[in] weights Weights tensor. + * @param[in] bias Bias tensor. + * @param[out] dst Result tensor. + */ + static void fully_connected_layer(const RawTensor &src, const RawTensor &weights, const RawTensor &bias, RawTensor &dst); + /** Normalization of @p src based on the information from @p norm_info. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + * @param[in] norm_info Normalization Layer information. + */ + static void normalization_layer(const RawTensor &src, RawTensor &dst, NormalizationLayerInfo norm_info); + /** Pooling layer of @p src based on the information from @p norm_info. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + * @param[in] pool_info Pooling Layer information. + * @param[in] fixed_point_position Fixed point position. (Optional) + */ + static void pooling_layer(const RawTensor &src, RawTensor &dst, PoolingLayerInfo pool_info, int fixed_point_position = 0); + /** Softmax Layer of @p src. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + */ + static void softmax_layer(const RawTensor &src, RawTensor &dst); + /** Fixed point operations of @p src + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + * @param[in] op Fixed point operation to perform. + */ + static void fixed_point_operation(const RawTensor &src, RawTensor &dst, FixedPointOp op); + +private: + ReferenceCPP() = delete; + ~ReferenceCPP() = delete; +}; +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif diff --git a/tests/validation/Tensor.h b/tests/validation/Tensor.h new file mode 100644 index 0000000000..81066b40ad --- /dev/null +++ b/tests/validation/Tensor.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_TENSOR_H__ +#define __ARM_COMPUTE_TEST_TENSOR_H__ + +#include "arm_compute/core/TensorShape.h" +#include "arm_compute/core/Types.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +template <typename T> +class Tensor +{ +public: + Tensor() + : _shape(), _dt(DataType::UNKNOWN), _fixed_point_position(0), _ptr(nullptr), _ptr_const(nullptr) {}; + + Tensor(TensorShape shape, DataType dt, int fixed_point_position, T *ptr) + : _shape(shape), _dt(dt), _fixed_point_position(fixed_point_position), _ptr(ptr), _ptr_const(nullptr) {}; + + Tensor(TensorShape shape, DataType dt, int fixed_point_position, const T *ptr) + : _shape(shape), _dt(dt), _fixed_point_position(fixed_point_position), _ptr(nullptr), _ptr_const(ptr) {}; + + Tensor(const Tensor &tensor) = delete; + Tensor &operator=(const Tensor &) = delete; + Tensor(Tensor &&) = default; + Tensor &operator=(Tensor &&) = default; + + ~Tensor() = default; + + T &operator[](size_t offset) + { + return _ptr[offset]; + } + + const T &operator[](size_t offset) const + { + return _ptr_const[offset]; + } + + int num_elements() const + { + return std::accumulate(_shape.cbegin(), _shape.cend(), 1, std::multiplies<int>()); + } + + TensorShape shape() const + { + return _shape; + } + + DataType data_type() const + { + return _dt; + } + + int fixed_point_position() const + { + return _fixed_point_position; + } + + const T *data() const + { + return (_ptr != nullptr) ? _ptr : _ptr_const; + } + T *data() + { + return _ptr; + } + + const T *data_const() + { + return _ptr_const; + } + +private: + TensorShape _shape; + DataType _dt; + int _fixed_point_position; + T *_ptr; + const T *_ptr_const; +}; +} // namespace validation +} // test +} // arm_compute + +#endif /* __ARM_COMPUTE_TEST_TENSOR_H__ */ diff --git a/tests/validation/TensorFactory.h b/tests/validation/TensorFactory.h new file mode 100644 index 0000000000..48f9d6702f --- /dev/null +++ b/tests/validation/TensorFactory.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_TENSOR_FACTORY_H__ +#define __ARM_COMPUTE_TEST_TENSOR_FACTORY_H__ + +#include "RawTensor.h" +#include "Tensor.h" +#include "arm_compute/core/Error.h" + +#include "boost_wrapper.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +using TensorVariant = boost::variant < Tensor<uint8_t>, Tensor<int8_t>, + Tensor<uint16_t>, Tensor<int16_t>, + Tensor<uint32_t>, Tensor<int32_t>, +#ifdef ENABLE_FP16 + Tensor<float16_t>, +#endif + Tensor<float >>; + +/** Helper to create a constant type if the passed reference is constant. */ +template <typename R, typename T> +struct match_const +{ + using type = typename std::conditional<std::is_const<typename std::remove_reference<R>::type>::value, const T, T>::type; +}; + +class TensorFactory +{ +public: + template <typename R> + static TensorVariant get_tensor(R &&raw) + { + TensorVariant v; + DataType dt = raw.data_type(); + int fixed_point_position = raw.fixed_point_position(); + auto shape = raw.shape(); + auto data = raw.data(); + + switch(dt) + { + case DataType::U8: + using value_type_u8 = typename match_const<R, uint8_t>::type; + v = Tensor<uint8_t>(shape, dt, fixed_point_position, reinterpret_cast<value_type_u8 *>(data)); + break; + case DataType::S8: + case DataType::QS8: + using value_type_s8 = typename match_const<R, int8_t>::type; + v = Tensor<int8_t>(shape, dt, fixed_point_position, reinterpret_cast<value_type_s8 *>(data)); + break; + case DataType::U16: + using value_type_u16 = typename match_const<R, uint16_t>::type; + v = Tensor<uint16_t>(shape, dt, fixed_point_position, reinterpret_cast<value_type_u16 *>(data)); + break; + case DataType::S16: + using value_type_s16 = typename match_const<R, int16_t>::type; + v = Tensor<int16_t>(shape, dt, fixed_point_position, reinterpret_cast<value_type_s16 *>(data)); + break; + case DataType::U32: + using value_type_u32 = typename match_const<R, uint32_t>::type; + v = Tensor<uint32_t>(shape, dt, fixed_point_position, reinterpret_cast<value_type_u32 *>(data)); + break; + case DataType::S32: + using value_type_s32 = typename match_const<R, int32_t>::type; + v = Tensor<int32_t>(shape, dt, fixed_point_position, reinterpret_cast<value_type_s32 *>(data)); + break; +#ifdef ENABLE_FP16 + case DataType::F16: + using value_type_f16 = typename match_const<R, float16_t>::type; + v = Tensor<float16_t>(raw.shape(), dt, reinterpret_cast<value_type_f16 *>(raw.data())); + break; +#endif + case DataType::F32: + using value_type_f32 = typename match_const<R, float>::type; + v = Tensor<float>(shape, dt, fixed_point_position, reinterpret_cast<value_type_f32 *>(data)); + break; + default: + ARM_COMPUTE_ERROR("NOT SUPPORTED!"); + } + return v; + } +}; +} // namespace validation +} // namespace test +} // namespace arm_compute + +#endif /* __ARM_COMPUTE_TEST_TENSOR_FACTORY_H__ */ diff --git a/tests/validation/TensorOperations.h b/tests/validation/TensorOperations.h new file mode 100644 index 0000000000..5e27e9d3a0 --- /dev/null +++ b/tests/validation/TensorOperations.h @@ -0,0 +1,1370 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_TENSOR_OPERATIONS_H__ +#define __ARM_COMPUTE_TEST_TENSOR_OPERATIONS_H__ + +#include "FixedPoint.h" +#include "Tensor.h" +#include "Types.h" +#include "Utils.h" + +#include "FixedPoint.h" +#include "Types.h" +#include "arm_compute/core/FixedPoint.h" +#include "arm_compute/core/Types.h" +#include "tests/validation/FixedPoint.h" + +#include <algorithm> +#include <array> +#include <cmath> + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace tensor_operations +{ +namespace +{ +bool is_valid_pixel(int i, int min, int max) +{ + return (i >= min && i < max); +} + +// 3D convolution for floating point type +template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type * = nullptr> +void convolution3d(const T *in, const T *weights, const T *bias, T *out, int xi, int yi, int width_in, int height_in, int depth_in, int width_weights, int height_weights, int8_t fixed_point_position) +{ + const int half_width_weights = width_weights / 2; + const int half_height_weights = height_weights / 2; + + // Reset accumulator + T acc = static_cast<T>(0); + + // Compute a 2D convolution for each IFM and accumulate the result + for(int ifm = 0; ifm < depth_in; ++ifm) + { + // Compute the offset for the input slice + const int offset_slice_in = xi + yi * width_in + ifm * width_in * height_in; + + // Compute 2D convolution + for(int yk = -half_height_weights; yk <= half_height_weights; ++yk) + { + for(int xk = -half_width_weights; xk <= half_width_weights; ++xk) + { + // Check if the pixel is out-of-bound + if(is_valid_pixel(xi + xk, 0, width_in) && is_valid_pixel(yi + yk, 0, height_in)) + { + const int idx = xk + half_width_weights; + const int idy = yk + half_height_weights; + + const T i_value = in[offset_slice_in + xk + yk * width_in]; + const T w_value = weights[idx + idy * width_weights + ifm * width_weights * height_weights]; + + acc += i_value * w_value; + } + } + } + } + + // Accumulate the bias and store the result + *out = acc + (*bias); +} + +// 3D convolution for fixed point type +template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type * = nullptr> +void convolution3d(const T *in, const T *weights, const T *bias, T *out, int xi, int yi, int width_in, int height_in, int depth_in, int width_weights, int height_weights, + int8_t fixed_point_position) +{ + const int half_width_weights = width_weights / 2; + const int half_height_weights = height_weights / 2; + + using namespace fixed_point_arithmetic; + using promoted_type = typename fixed_point_arithmetic::traits::promote<T>::type; + + // Reset accumulator + fixed_point<promoted_type> acc(0, fixed_point_position); + + // Compute a 2D convolution for each IFM and accumulate the result + for(int ifm = 0; ifm < depth_in; ++ifm) + { + // Compute the offset for the input slice + const int offset_slice_in = xi + yi * width_in + ifm * width_in * height_in; + + // Compute 2D convolution + for(int yk = -half_height_weights; yk <= half_height_weights; ++yk) + { + for(int xk = -half_width_weights; xk <= half_width_weights; ++xk) + { + // Check if the pixel is out-of-bound + if(is_valid_pixel(xi + xk, 0, width_in) && is_valid_pixel(yi + yk, 0, height_in)) + { + const int idx = xk + half_width_weights; + const int idy = yk + half_height_weights; + + const fixed_point<promoted_type> i_value(in[offset_slice_in + xk + yk * width_in], fixed_point_position, true); + const fixed_point<promoted_type> w_value(weights[idx + idy * width_weights + ifm * width_weights * height_weights], fixed_point_position, true); + const fixed_point<promoted_type> iw = i_value * w_value; + acc = iw + acc; + } + } + } + } + + // Get the bias + const fixed_point<promoted_type> b(*bias, fixed_point_position, true); + + // Accumulate the bias and covert back + acc = acc + b; + fixed_point<T> res(acc); + *out = res.raw(); +} + +template <typename T> +void vector_matrix_multiply(const T *in, const T *weights, const T *bias, T *out, int cols_weights, int rows_weights, uint8_t fixed_point_position) +{ + for(int x = 0; x < cols_weights; ++x) + { + T acc = 0.0f; + for(int y = 0; y < rows_weights; ++y) + { + acc += in[y] * weights[x + y * cols_weights]; + } + out[x] = acc + bias[x]; + } +} + +template <> +void vector_matrix_multiply(const int8_t *in, const int8_t *weights, const int8_t *bias, int8_t *out, int cols_weights, int rows_weights, uint8_t fixed_point_position) +{ + using namespace fixed_point_arithmetic; + using promoted_type = typename fixed_point_arithmetic::traits::promote<int8_t>::type; + + for(int x = 0; x < cols_weights; ++x) + { + // Reset accumulator + fixed_point<promoted_type> acc(0, fixed_point_position); + + for(int y = 0; y < rows_weights; ++y) + { + const fixed_point<promoted_type> i_value(in[y], fixed_point_position, true); + const fixed_point<promoted_type> w_value(weights[x + y * cols_weights], fixed_point_position, true); + const fixed_point<promoted_type> iw = i_value * w_value; + acc = iw + acc; + } + + // Get the bias + const fixed_point<int8_t> b(bias[x], fixed_point_position, true); + + // Convert back and accumulate the bias + fixed_point<int8_t> res(acc); + res = res + b; + + // Store the result + out[x] = res.raw(); + } +} + +/** Apply 2D spatial filter on a single element of @p in at coordinates @p coord + * + * - filter sizes have to be odd number + * - Valid region assumed + * - Row major order of filter assumed + * - TO_ZERO rounding policy assumed + * - SATURATE convert policy assumed + * + */ +template <typename T1, typename T2, typename T3> +void apply_2d_spatial_filter(Coordinates coord, const Tensor<T1> &in, Tensor<T3> &out, const TensorShape &filter_shape, const T2 *filter_itr, float scale) +{ + using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type; + intermediate_type val = 0; + int x = coord.x(); + int y = coord.y(); + for(size_t j = y - filter_shape[1] / 2; j <= y + filter_shape[1] / 2; ++j) + { + for(size_t i = x - filter_shape[0] / 2; i <= x + filter_shape[0] / 2; ++i) + { + coord.set(0, i); + coord.set(1, j); + val += static_cast<intermediate_type>(*filter_itr) * static_cast<intermediate_type>(in[coord2index(in.shape(), coord)]); + ++filter_itr; + } + } + coord.set(0, x); + coord.set(1, y); + double rounded_val = cpp11::trunc(val * static_cast<double>(scale)); + out[coord2index(in.shape(), coord)] = saturate_cast<T3>(rounded_val); +} +} // namespace + +// Integral Image +void integral_image(const Tensor<uint8_t> &in, Tensor<uint32_t> &out) +{ + // Length of dimensions + const size_t width = in.shape().x(); + const size_t height = in.shape().y(); + const size_t depth = in.shape().z() * in.shape()[3] * in.shape()[4] * in.shape()[5]; + + const size_t image_size = width * height; + + for(size_t z = 0; z < depth; ++z) + { + size_t current_image = z * image_size; + + //First element of each image + out[current_image] = in[current_image]; + + // First row of each image (add only pixel on the left) + for(size_t x = 1; x < width; ++x) + { + out[current_image + x] = static_cast<uint32_t>(in[current_image + x]) + out[current_image + x - 1]; + } + + // Subsequent rows + for(size_t y = 1; y < height; ++y) + { + size_t current_row = current_image + (width * y); + + // First element of each row (add only pixel up) + out[current_row] = static_cast<uint32_t>(in[current_row]) + out[current_row - width]; + + // Following row elements + for(size_t x = 1; x < width; ++x) + { + size_t current_pixel = current_row + x; + + // out = in + up(out) + left(out) - up_left(out) + out[current_pixel] = static_cast<uint32_t>(in[current_pixel]) + out[current_pixel - 1] + + out[current_pixel - width] - out[current_pixel - width - 1]; + } + } + } +} + +// Absolute difference +template <typename T1, typename T2, typename T3> +void absolute_difference(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out) +{ + using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type; + + for(int i = 0; i < in1.num_elements(); ++i) + { + intermediate_type val = std::abs(static_cast<intermediate_type>(in1[i]) - static_cast<intermediate_type>(in2[i])); + out[i] = saturate_cast<T3>(val); + } +} + +// Accumulate +template <typename T1, typename T2> +void accumulate(const Tensor<T1> &in, Tensor<T2> &out) +{ + using intermediate_type = typename common_promoted_signed_type<T1, T2>::intermediate_type; + + for(int i = 0; i < in.num_elements(); ++i) + { + intermediate_type val = static_cast<intermediate_type>(out[i]) + static_cast<intermediate_type>(in[i]); + out[i] = saturate_cast<T2>(val); + } +} + +// Accumulate squared +template <typename T1, typename T2> +void accumulate_squared(const Tensor<T1> &in, Tensor<T2> &out, uint32_t shift) +{ + if(shift > 15) + { + ARM_COMPUTE_ERROR("Shift in accumulate_squared must be within the range [0, 15]"); + } + using intermediate_type = typename common_promoted_signed_type<T1, T2>::intermediate_type; + intermediate_type denom = 1 << shift; + + for(int i = 0; i < in.num_elements(); ++i) + { + intermediate_type val = static_cast<intermediate_type>(out[i]) + (static_cast<intermediate_type>(in[i]) * static_cast<intermediate_type>(in[i]) / denom); + out[i] = saturate_cast<T2>(val); + } +} + +// Accumulate weighted +template <typename T> +void accumulate_weighted(const Tensor<T> &in, Tensor<T> &out, float alpha) +{ + if(alpha < 0.f || alpha > 1.f) + { + ARM_COMPUTE_ERROR("Weight (alpha) specified in accumulate_weighted must be within the range [0, 1]"); + } + using intermediate_type = typename common_promoted_signed_type<T>::intermediate_type; + + for(int i = 0; i < in.num_elements(); ++i) + { + double val = (1. - static_cast<double>(alpha)) * static_cast<intermediate_type>(out[i]) + static_cast<double>(alpha) * static_cast<intermediate_type>(in[i]); + out[i] = static_cast<T>(val); + } +} + +// Arithmetic addition +template <typename T1, typename T2, typename T3> +void arithmetic_addition(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out, ConvertPolicy convert_policy) +{ + using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type; + + for(int i = 0; i < in1.num_elements(); ++i) + { + intermediate_type val = static_cast<intermediate_type>(in1[i]) + static_cast<intermediate_type>(in2[i]); + out[i] = (convert_policy == ConvertPolicy::SATURATE) ? saturate_cast<T3>(val) : static_cast<T3>(val); + } +} + +// Arithmetic Subtraction +template <typename T1, typename T2, typename T3> +void arithmetic_subtraction(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out, ConvertPolicy convert_policy) +{ + using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type; + + for(int i = 0; i < in1.num_elements(); ++i) + { + intermediate_type val = static_cast<intermediate_type>(in1[i]) - static_cast<intermediate_type>(in2[i]); + out[i] = (convert_policy == ConvertPolicy::SATURATE) ? saturate_cast<T3>(val) : static_cast<T3>(val); + } +} + +// Bitwise and +template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> +void bitwise_and(const Tensor<T> &in1, const Tensor<T> &in2, Tensor<T> &out) +{ + for(int i = 0; i < in1.num_elements(); ++i) + { + out[i] = in1[i] & in2[i]; + } +} + +// Bitwise or +template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> +void bitwise_or(const Tensor<T> &in1, const Tensor<T> &in2, Tensor<T> &out) +{ + for(int i = 0; i < in1.num_elements(); ++i) + { + out[i] = in1[i] | in2[i]; + } +} + +// Bitwise xor +template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> +void bitwise_xor(const Tensor<T> &in1, const Tensor<T> &in2, Tensor<T> &out) +{ + for(int i = 0; i < in1.num_elements(); ++i) + { + out[i] = in1[i] ^ in2[i]; + } +} + +// Bitwise not +template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> +void bitwise_not(const Tensor<T> &in, Tensor<T> &out) +{ + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = ~in[i]; + } +} + +// 3-by-3 box filter +template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> +void box3x3(const Tensor<T> &in, Tensor<T> &out) +{ + const std::array<T, 9> filter{ { 1, 1, 1, 1, 1, 1, 1, 1, 1 } }; + float scale = 1.f / static_cast<float>(filter.size()); + const ValidRegion valid_region = shape_to_valid_region_undefined_border(in.shape(), BorderSize(1)); + for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx) + { + const Coordinates id = index2coord(in.shape(), element_idx); + if(is_in_valid_region(valid_region, id)) + { + apply_2d_spatial_filter(id, in, out, TensorShape(3U, 3U), filter.data(), scale); + } + } +} + +// Depth conversion +template <typename T1, typename T2> +void depth_convert(const Tensor<T1> &in, Tensor<T2> &out, ConvertPolicy policy, uint32_t shift) +{ + ARM_COMPUTE_ERROR("The conversion is not supported"); +} + +template <> +void depth_convert<int8_t, float>(const Tensor<int8_t> &in, Tensor<float> &out, ConvertPolicy policy, uint32_t shift) +{ + const int8_t fixed_point_position = static_cast<int8_t>(in.fixed_point_position()); + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = static_cast<float>(in[i]) * (1.0f / (1 << fixed_point_position)); + } +} + +template <> +void depth_convert<float, int8_t>(const Tensor<float> &in, Tensor<int8_t> &out, ConvertPolicy policy, uint32_t shift) +{ + const int8_t fixed_point_position = static_cast<int8_t>(in.fixed_point_position()); + for(int i = 0; i < in.num_elements(); ++i) + { + float val = in[i] * (1 << fixed_point_position) + 0.5f; + out[i] = ((policy == ConvertPolicy::SATURATE) ? saturate_cast<int8_t>(val) : static_cast<int8_t>(val)); + } +} + +template <> +void depth_convert<uint8_t, uint16_t>(const Tensor<uint8_t> &in, Tensor<uint16_t> &out, ConvertPolicy policy, uint32_t shift) +{ + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = static_cast<uint16_t>(in[i]) << shift; + } +} + +template <> +void depth_convert<uint8_t, int16_t>(const Tensor<uint8_t> &in, Tensor<int16_t> &out, ConvertPolicy policy, uint32_t shift) +{ + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = static_cast<int16_t>(in[i]) << shift; + } +} + +template <> +void depth_convert<uint8_t, int32_t>(const Tensor<uint8_t> &in, Tensor<int32_t> &out, ConvertPolicy policy, uint32_t shift) +{ + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = static_cast<int32_t>(in[i]) << shift; + } +} + +template <> +void depth_convert<uint16_t, uint8_t>(const Tensor<uint16_t> &in, Tensor<uint8_t> &out, ConvertPolicy policy, uint32_t shift) +{ + for(int i = 0; i < in.num_elements(); ++i) + { + uint16_t val = in[i] >> shift; + out[i] = ((policy == ConvertPolicy::SATURATE) ? saturate_cast<uint8_t>(val) : static_cast<uint8_t>(val)); + } +} + +template <> +void depth_convert<uint16_t, uint32_t>(const Tensor<uint16_t> &in, Tensor<uint32_t> &out, ConvertPolicy policy, uint32_t shift) +{ + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = static_cast<uint32_t>(in[i]) << shift; + } +} + +template <> +void depth_convert<int16_t, uint8_t>(const Tensor<int16_t> &in, Tensor<uint8_t> &out, ConvertPolicy policy, uint32_t shift) +{ + for(int i = 0; i < in.num_elements(); ++i) + { + int16_t val = in[i] >> shift; + out[i] = ((policy == ConvertPolicy::SATURATE) ? saturate_cast<uint8_t>(val) : static_cast<uint8_t>(val)); + } +} +template <> +void depth_convert<int16_t, int32_t>(const Tensor<int16_t> &in, Tensor<int32_t> &out, ConvertPolicy policy, uint32_t shift) +{ + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = static_cast<int32_t>(in[i]) << shift; + } +} + +// Matrix multiplication for floating point type +template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type * = nullptr> +void gemm(const Tensor<T> &in1, const Tensor<T> &in2, const Tensor<T> &in3, Tensor<T> &out, float alpha, float beta) +{ + const int M = out.shape().y(); + const int N = out.shape().x(); + const int K = in1.shape().x(); + + for(int r = 0; r < M; ++r) + { + for(int c = 0; c < N; ++c) + { + T acc = 0.0f; + + for(int k = 0; k < K; ++k) + { + const T a0 = in1[r * K + k]; + const T b0 = in2[k * N + c]; + + acc += a0 * b0; + } + + // Finalize the result: A * B * alpha + C * beta + const T c0 = in3[c + r * N]; + out[c + r * N] = alpha * acc + beta * c0; + } + } +} + +// Matrix multiplication for fixed point type +template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type * = nullptr> +void gemm(const Tensor<T> &in1, const Tensor<T> &in2, const Tensor<T> &in3, Tensor<T> &out, float alpha, float beta) +{ + using namespace fixed_point_arithmetic; + + using promoted_type = typename fixed_point_arithmetic::traits::promote<T>::type; + + const int M = out.shape().y(); + const int N = out.shape().x(); + const int K = in1.shape().x(); + const int8_t fixed_point_position = static_cast<int8_t>(in1.fixed_point_position()); + + const fixed_point<T> alpha_q(alpha, fixed_point_position); + const fixed_point<T> beta_q(beta, fixed_point_position); + + for(int r = 0; r < M; ++r) + { + for(int c = 0; c < N; ++c) + { + fixed_point<promoted_type> acc_q(0, fixed_point_position); + + for(int k = 0; k < K; ++k) + { + const fixed_point<promoted_type> a0_q(in1[r * K + k], fixed_point_position, true); + const fixed_point<promoted_type> b0_q(in2[k * N + c], fixed_point_position, true); + const fixed_point<promoted_type> axb_q = a0_q * b0_q; + + acc_q = axb_q + acc_q; + } + + // Finalize the result: A * B * alpha + C * beta + const fixed_point<T> c0_q(in3[c + r * N], fixed_point_position, true); + + fixed_point<T> res_q(acc_q); + res_q = alpha_q * res_q; + res_q = (c0_q * beta_q) + res_q; + + // Store the result + out[c + r * N] = res_q.raw(); + } + } +} + +// Pixel-wise multiplication +template <typename T1, typename T2, typename T3> +void pixel_wise_multiplication(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) +{ + if(scale < 0) + { + ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative"); + } + using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type; + for(int i = 0; i < in1.num_elements(); ++i) + { + double val = static_cast<intermediate_type>(in1[i]) * static_cast<intermediate_type>(in2[i]) * static_cast<double>(scale); + if(std::is_floating_point<T3>::value) + { + out[i] = val; + } + else + { + double rounded_val = 0; + switch(rounding_policy) + { + case(RoundingPolicy::TO_ZERO): + rounded_val = cpp11::trunc(val); + break; + case(RoundingPolicy::TO_NEAREST_UP): + rounded_val = cpp11::round_half_up(val); + break; + case(RoundingPolicy::TO_NEAREST_EVEN): + rounded_val = cpp11::round_half_even(val); + break; + default: + ARM_COMPUTE_ERROR("Unsupported rounding policy"); + } + out[i] = (convert_policy == ConvertPolicy::SATURATE) ? saturate_cast<T3>(rounded_val) : static_cast<T3>(rounded_val); + } + } +} + +// Fixed-point Pixel-wise Multiplication +template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> +void fixed_point_pixel_wise_multiplication(const Tensor<T> &in1, const Tensor<T> &in2, Tensor<T> &out, int scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) +{ + using namespace fixed_point_arithmetic; + + const int fixed_point_position = in1.fixed_point_position(); + + ARM_COMPUTE_ERROR_ON_MSG(in1.data_type() != in2.data_type() || in1.data_type() != out.data_type(), + "Tensors must all have the same DataType"); + ARM_COMPUTE_ERROR_ON_MSG(fixed_point_position != in2.fixed_point_position() || fixed_point_position != out.fixed_point_position(), + "Fixed-point position must be the same for both inputs and outputs"); + + // Validate fixed_point_position + ARM_COMPUTE_ERROR_ON((in1.data_type() == DataType::QS8) && (fixed_point_position == 0 || fixed_point_position > 7)); + ARM_COMPUTE_ERROR_ON((in1.data_type() == DataType::QS16) && (fixed_point_position == 0 || fixed_point_position > 15)); + + fixed_point<T> fp_scale(scale, fixed_point_position); + const bool is_sat = convert_policy == ConvertPolicy::SATURATE; + const bool do_scaling = scale != 1; + + for(int i = 0; i < in1.num_elements(); ++i) + { + fixed_point<T> val1(in1[i], fixed_point_position, true); + fixed_point<T> val2(in2[i], fixed_point_position, true); + fixed_point<T> res = (is_sat) ? val1 * val2 : mul<OverflowPolicy::WRAP>(val1, val2); + if(do_scaling) + { + res = (is_sat) ? res * fp_scale : mul<OverflowPolicy::WRAP>(res, fp_scale); + } + out[i] = res.raw(); + } +} + +// Threshold +template <typename T> +void threshold(const Tensor<T> &in, Tensor<T> &out, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper) +{ + switch(type) + { + case ThresholdType::BINARY: + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = ((in[i] > threshold) ? true_value : false_value); + } + break; + case ThresholdType::RANGE: + for(int i = 0; i < in.num_elements(); ++i) + { + if(in[i] > upper) + { + out[i] = false_value; + } + else if(in[i] < threshold) + { + out[i] = false_value; + } + else + { + out[i] = true_value; + } + } + break; + default: + ARM_COMPUTE_ERROR("Thresholding type not recognised"); + break; + } +} + +// Activation Layer for floating point type +template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type * = nullptr> +void activation_layer(const Tensor<T> &in, Tensor<T> &out, ActivationLayerInfo act_info) +{ + const T a = static_cast<T>(act_info.a()); + const T b = static_cast<T>(act_info.b()); + + for(int i = 0; i < in.num_elements(); ++i) + { + T x = in[i]; + switch(act_info.activation()) + { + case ActivationLayerInfo::ActivationFunction::ABS: + out[i] = std::abs(x); + break; + case ActivationLayerInfo::ActivationFunction::BOUNDED_RELU: + out[i] = std::min<T>(a, std::max<T>(0, x)); + break; + case ActivationLayerInfo::ActivationFunction::LINEAR: + out[i] = a * x + b; + break; + case ActivationLayerInfo::ActivationFunction::LOGISTIC: + out[i] = static_cast<T>(1) / (static_cast<T>(1) + std::exp(-x)); + break; + case ActivationLayerInfo::ActivationFunction::RELU: + out[i] = std::max<T>(0, x); + break; + case ActivationLayerInfo::ActivationFunction::SOFT_RELU: + out[i] = std::log(static_cast<T>(1) + std::exp(x)); + break; + case ActivationLayerInfo::ActivationFunction::SQRT: + out[i] = std::sqrt(x); + break; + case ActivationLayerInfo::ActivationFunction::SQUARE: + out[i] = x * x; + break; + case ActivationLayerInfo::ActivationFunction::TANH: + out[i] = a * std::tanh(b * x); + break; + default: + ARM_COMPUTE_ERROR("Activation function not recognised"); + break; + } + } +} + +// Activation Layer for fixed point type +template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type * = nullptr> +void activation_layer(const Tensor<T> &in, Tensor<T> &out, ActivationLayerInfo act_info) +{ + using namespace fixed_point_arithmetic; + int fixed_point_position = in.fixed_point_position(); + ActivationLayerInfo::ActivationFunction act_func = act_info.activation(); + const fixed_point<T> a(act_info.a(), fixed_point_position); + const fixed_point<T> b(act_info.b(), fixed_point_position); + const fixed_point<T> const_0(0, fixed_point_position); + const fixed_point<T> const_1(1, fixed_point_position); + + for(int i = 0; i < in.num_elements(); ++i) + { + fixed_point<T> x(in[i], fixed_point_position, true); + switch(act_func) + { + case ActivationLayerInfo::ActivationFunction::ABS: + out[i] = abs(x).raw(); + break; + case ActivationLayerInfo::ActivationFunction::BOUNDED_RELU: + out[i] = min(a, max(const_0, x)).raw(); + break; + case ActivationLayerInfo::ActivationFunction::LINEAR: + out[i] = add(b, mul(a, x)).raw(); + break; + case ActivationLayerInfo::ActivationFunction::LOGISTIC: + out[i] = (const_1 / (const_1 + exp(-x))).raw(); + break; + case ActivationLayerInfo::ActivationFunction::RELU: + out[i] = max(const_0, x).raw(); + break; + case ActivationLayerInfo::ActivationFunction::SOFT_RELU: + out[i] = log(const_1 + exp(x)).raw(); + break; + case ActivationLayerInfo::ActivationFunction::SQRT: + out[i] = (const_1 / inv_sqrt(x)).raw(); + break; + case ActivationLayerInfo::ActivationFunction::SQUARE: + out[i] = mul(x, x).raw(); + break; + case ActivationLayerInfo::ActivationFunction::TANH: + out[i] = tanh(x).raw(); + break; + default: + ARM_COMPUTE_ERROR("Activation function not recognised"); + break; + } + } +} + +// Batch Normalization Layer for fixed point type +template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type * = nullptr> +void batch_normalization_layer(const Tensor<T> &in, Tensor<T> &out, const Tensor<T> &mean, const Tensor<T> &var, const Tensor<T> &beta, const Tensor<T> &gamma, float epsilon, int fixed_point_position) +{ + const int cols = static_cast<int>(in.shape()[0]); + const int rows = static_cast<int>(in.shape()[1]); + const int depth = static_cast<int>(in.shape()[2]); + int upper_dims = in.shape().total_size() / (cols * rows * depth); + + for(int r = 0; r < upper_dims; ++r) + { + for(int i = 0; i < depth; ++i) + { + for(int k = 0; k < rows; ++k) + { + for(int l = 0; l < cols; ++l) + { + const int pos = l + k * cols + i * rows * cols + r * cols * rows * depth; + fixed_point_arithmetic::fixed_point<T> in_qs8(in[pos], fixed_point_position, true); + fixed_point_arithmetic::fixed_point<T> var_qs8(var[i], fixed_point_position, true); + fixed_point_arithmetic::fixed_point<T> mean_qs8(mean[i], fixed_point_position, true); + fixed_point_arithmetic::fixed_point<T> beta_qs8(beta[i], fixed_point_position, true); + fixed_point_arithmetic::fixed_point<T> gamma_qs8(gamma[i], fixed_point_position, true); + fixed_point_arithmetic::fixed_point<T> epsilon_qs8(epsilon, fixed_point_position); + + auto denominator = fixed_point_arithmetic::inv_sqrt(var_qs8 + epsilon_qs8); + auto numerator = in_qs8 - mean_qs8; + auto x_bar = numerator * denominator; + x_bar = beta_qs8 + x_bar * gamma_qs8; + out[pos] = x_bar.raw(); + } + } + } + } +} + +// Batch Normalization Layer for floating point type +template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type * = nullptr> +void batch_normalization_layer(const Tensor<T> &in, Tensor<T> &out, const Tensor<T> &mean, const Tensor<T> &var, const Tensor<T> &beta, const Tensor<T> &gamma, float epsilon, int fixed_point_position) +{ + const int cols = static_cast<int>(in.shape()[0]); + const int rows = static_cast<int>(in.shape()[1]); + const int depth = static_cast<int>(in.shape()[2]); + int upper_dims = in.shape().total_size() / (cols * rows * depth); + + for(int r = 0; r < upper_dims; ++r) + { + for(int i = 0; i < depth; ++i) + { + for(int k = 0; k < rows; ++k) + { + for(int l = 0; l < cols; ++l) + { + const int pos = l + k * cols + i * rows * cols + r * cols * rows * depth; + const float denominator = sqrt(var[i] + epsilon); + const float numerator = in[pos] - mean[i]; + const float x_bar = numerator / denominator; + out[pos] = beta[i] + x_bar * gamma[i]; + } + } + } + } +} + +// Convolution layer +template <typename T> +void convolution_layer(const Tensor<T> &in, const Tensor<T> &weights, const Tensor<T> &bias, Tensor<T> &out, const PadStrideInfo &conv_info) +{ + const int width_in = in.shape().x(); + const int height_in = in.shape().y(); + const int depth_in = in.shape().z(); + const int width_out = out.shape().x(); + const int height_out = out.shape().y(); + const int depth_out = out.shape().z(); + const int width_weights = weights.shape().x(); + const int height_weights = weights.shape().y(); + const int depth_weights = weights.shape().z(); + const int pad_xi = std::min(static_cast<int>(conv_info.pad().first), width_weights / 2); + const int pad_yi = std::min(static_cast<int>(conv_info.pad().second), height_weights / 2); + const int start_xi = width_weights / 2 - pad_xi; + const int start_yi = height_weights / 2 - pad_yi; + const int end_xi = width_in - start_xi; + const int end_yi = height_in - start_yi; + const int stride_xi = conv_info.stride().first; + const int stride_yi = conv_info.stride().second; + const int num_batches = in.shape().total_size() / (width_in * height_in * depth_in); + + for(int r = 0; r < num_batches; ++r) + { + for(int yi = start_yi; yi < end_yi; yi += stride_yi) + { + for(int xi = start_xi; xi < end_xi; xi += stride_xi) + { + for(int ofm = 0; ofm < depth_out; ++ofm) + { + // Compute input and output offsets + const int offset_in = r * width_in * height_in * depth_in; + const int xo = (xi - start_xi) / stride_xi; + const int yo = (yi - start_yi) / stride_yi; + const int offset_out = xo + yo * width_out + ofm * width_out * height_out + r * width_out * height_out * depth_out; + + // Compute 3D convolution + convolution3d(in.data() + offset_in, + weights.data() + ofm * width_weights * height_weights * depth_weights, + bias.data() + ofm, + out.data() + offset_out, + xi, yi, + width_in, height_in, depth_in, + width_weights, height_weights, + static_cast<int8_t>(in.fixed_point_position())); + } + } + } + } +} + +// Fully connected layer +template <typename T> +void fully_connected_layer(const Tensor<T> &in, const Tensor<T> &weights, const Tensor<T> &bias, Tensor<T> &out) +{ + ARM_COMPUTE_ERROR_ON(weights.shape().x() != out.shape().x()); + ARM_COMPUTE_ERROR_ON(weights.shape().y() != in.shape().x() * in.shape().y() * in.shape().z()); + const int cols_weights = weights.shape().x(); + const int rows_weights = weights.shape().y(); + const int num_batches = in.shape().total_size() / rows_weights; + + for(int k = 0; k < num_batches; ++k) + { + vector_matrix_multiply<T>(in.data() + k * rows_weights, + weights.data(), + bias.data(), + out.data() + k * cols_weights, + cols_weights, + rows_weights, + in.fixed_point_position()); + } +} + +// Normalization Layer for floating point type +template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type * = nullptr> +void normalization_layer(const Tensor<T> &in, Tensor<T> &out, NormalizationLayerInfo norm_info) +{ + const uint32_t norm_size = norm_info.norm_size(); + NormType type = norm_info.type(); + float beta = norm_info.beta(); + uint32_t kappa = norm_info.kappa(); + + const int cols = static_cast<int>(in.shape()[0]); + const int rows = static_cast<int>(in.shape()[1]); + const int depth = static_cast<int>(in.shape()[2]); + int upper_dims = in.shape().total_size() / (cols * rows); + + float coeff = norm_info.scale_coeff(); + int radius_cols = norm_size / 2; + // IN_MAP_1D and CROSS_MAP normalize over a single axis only + int radius_rows = (NormType::IN_MAP_2D == type) ? norm_size / 2 : 0; + + if(type == NormType::CROSS_MAP) + { + // Remove also depth from upper dimensions since it is the axes we want + // to use for normalization + upper_dims /= depth; + for(int r = 0; r < upper_dims; ++r) + { + for(int i = 0; i < rows; ++i) + { + for(int k = 0; k < cols; ++k) + { + for(int l = 0; l < depth; ++l) + { + float accumulated_scale = 0.f; + for(int j = -radius_cols; j <= radius_cols; ++j) + { + const int z = l + j; + if(z >= 0 && z < depth) + { + const T value = in[k + i * cols + z * rows * cols + r * cols * rows * depth]; + accumulated_scale += value * value; + } + } + out[k + i * cols + l * rows * cols + r * cols * rows * depth] = kappa + accumulated_scale * coeff; + } + } + } + } + } + else + { + for(int r = 0; r < upper_dims; ++r) + { + for(int i = 0; i < rows; ++i) + { + for(int k = 0; k < cols; ++k) + { + float accumulated_scale = 0.f; + for(int j = -radius_rows; j <= radius_rows; ++j) + { + const int y = i + j; + for(int l = -radius_cols; l <= radius_cols; ++l) + { + const int x = k + l; + if((x >= 0 && y >= 0) && (x < cols && y < rows)) + { + const T value = in[x + y * cols + r * cols * rows]; + accumulated_scale += value * value; + } + } + } + out[k + i * cols + r * cols * rows] = kappa + accumulated_scale * coeff; + } + } + } + } + + if(beta == 1.f) + { + for(int i = 0; i < out.num_elements(); ++i) + { + out[i] = in[i] / out[i]; + } + } + else if(beta == 0.5f) + { + for(int i = 0; i < out.num_elements(); ++i) + { + out[i] = in[i] / std::sqrt(out[i]); + } + } + else + { + for(int i = 0; i < out.num_elements(); ++i) + { + out[i] = in[i] * std::exp(std::log(out[i]) * -beta); + } + } +} +// Normalization Layer for fixed-point types +template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type * = nullptr> +void normalization_layer(const Tensor<T> &in, Tensor<T> &out, NormalizationLayerInfo norm_info) +{ + using namespace fixed_point_arithmetic; + + const int fixed_point_position = in.fixed_point_position(); + + const uint32_t norm_size = norm_info.norm_size(); + NormType type = norm_info.type(); + fixed_point<T> beta(norm_info.beta(), fixed_point_position); + fixed_point<T> kappa(norm_info.kappa(), fixed_point_position); + + const int cols = static_cast<int>(in.shape()[0]); + const int rows = static_cast<int>(in.shape()[1]); + const int depth = static_cast<int>(in.shape()[2]); + int upper_dims = in.shape().total_size() / (cols * rows); + + fixed_point<T> coeff(norm_info.scale_coeff(), fixed_point_position); + int radius_cols = norm_size / 2; + // IN_MAP_1D and CROSS_MAP normalize over a single axis only + int radius_rows = (NormType::IN_MAP_2D == type) ? norm_size / 2 : 0; + + if(type == NormType::CROSS_MAP) + { + // Remove also depth from upper dimensions since it is the axes we want + // to use for normalization + upper_dims /= depth; + for(int r = 0; r < upper_dims; ++r) + { + for(int i = 0; i < rows; ++i) + { + for(int k = 0; k < cols; ++k) + { + for(int l = 0; l < depth; ++l) + { + fixed_point<T> accumulated_scale(0.f, fixed_point_position); + for(int j = -radius_cols; j <= radius_cols; ++j) + { + const int z = l + j; + if(z >= 0 && z < depth) + { + const T value = in[k + i * cols + z * rows * cols + r * cols * rows * depth]; + const fixed_point<T> fp_value(value, fixed_point_position, true); + accumulated_scale = add(accumulated_scale, mul(fp_value, fp_value)); + } + } + accumulated_scale = add(kappa, mul(accumulated_scale, coeff)); + out[k + i * cols + l * rows * cols + r * cols * rows * depth] = accumulated_scale.raw(); + } + } + } + } + } + else + { + for(int r = 0; r < upper_dims; ++r) + { + for(int i = 0; i < rows; ++i) + { + for(int k = 0; k < cols; ++k) + { + fixed_point<T> accumulated_scale(0.f, fixed_point_position); + for(int j = -radius_rows; j <= radius_rows; ++j) + { + const int y = i + j; + for(int l = -radius_cols; l <= radius_cols; ++l) + { + const int x = k + l; + if((x >= 0 && y >= 0) && (x < cols && y < rows)) + { + const T value = in[x + y * cols + r * cols * rows]; + const fixed_point<T> fp_value(value, fixed_point_position, true); + accumulated_scale = add(accumulated_scale, mul(fp_value, fp_value)); + } + } + } + accumulated_scale = add(kappa, mul(accumulated_scale, coeff)); + out[k + i * cols + r * cols * rows] = accumulated_scale.raw(); + } + } + } + } + + if(norm_info.beta() == 1.f) + { + for(int i = 0; i < out.num_elements(); ++i) + { + fixed_point<T> res = div(fixed_point<T>(in[i], fixed_point_position, true), fixed_point<T>(out[i], fixed_point_position, true)); + out[i] = res.raw(); + } + } + else + { + const fixed_point<T> beta(norm_info.beta(), fixed_point_position); + for(int i = 0; i < out.num_elements(); ++i) + { + fixed_point<T> res = pow(fixed_point<T>(out[i], fixed_point_position, true), beta); + res = div(fixed_point<T>(in[i], fixed_point_position, true), res); + out[i] = res.raw(); + } + } +} + +// Pooling layer +template <typename T> +void pooling_layer(const Tensor<T> &in, Tensor<T> &out, PoolingLayerInfo pool_info, int fixed_point_position) +{ + const int pool_size = pool_info.pool_size(); + PoolingType type = pool_info.pool_type(); + int pool_stride_x = 0; + int pool_stride_y = 0; + int pad_x = 0; + int pad_y = 0; + std::tie(pool_stride_x, pool_stride_y) = pool_info.pad_stride_info().stride(); + std::tie(pad_x, pad_y) = pool_info.pad_stride_info().pad(); + + const int cols_in = static_cast<int>(in.shape()[0]); + const int rows_in = static_cast<int>(in.shape()[1]); + + const int cols_out = static_cast<int>(out.shape()[0]); + const int rows_out = static_cast<int>(out.shape()[1]); + + int upper_dims = in.shape().total_size() / (cols_in * rows_in); + + int pooled_height = static_cast<int>(ceil(static_cast<float>(rows_in + 2 * pad_x - pool_size) / pool_stride_x)) + 1; + int pooled_width = static_cast<int>(ceil(static_cast<float>(cols_in + 2 * pad_y - pool_size) / pool_stride_y)) + 1; + + if((pooled_height - 1) * pool_stride_x >= rows_in + pad_x) + { + --pooled_height; + } + if((pooled_width - 1) * pool_stride_y >= cols_in + pad_y) + { + --pooled_width; + } + + if(type == PoolingType::MAX) + { + for(int r = 0; r < upper_dims; ++r) + { + for(int i = 0; i < pooled_height; ++i) + { + for(int k = 0; k < pooled_width; ++k) + { + int hstart = i * pool_stride_x - pad_x; + int wstart = k * pool_stride_y - pad_y; + int hend = std::min(hstart + pool_size, rows_in); + int wend = std::min(wstart + pool_size, cols_in); + hstart = std::max(hstart, 0); + wstart = std::max(wstart, 0); + + T max_val = std::numeric_limits<T>::lowest(); + for(int y = hstart; y < hend; ++y) + { + for(int x = wstart; x < wend; ++x) + { + T val = in[r * cols_in * rows_in + y * cols_in + x]; + if(val > max_val) + { + max_val = val; + } + } + } + + out[r * rows_out * cols_out + i * pooled_width + k] = max_val; + } + } + } + } + else // Average pooling + { + for(int r = 0; r < upper_dims; ++r) + { + for(int i = 0; i < pooled_height; ++i) + { + for(int k = 0; k < pooled_width; ++k) + { + T avg_val = 0; + + int hstart = i * pool_stride_x - pad_x; + int wstart = k * pool_stride_y - pad_y; + int hend = std::min(hstart + pool_size, cols_in + pad_x); + int wend = std::min(wstart + pool_size, rows_in + pad_y); + int pool = (hend - hstart) * (wend - wstart); + hstart = std::max(hstart, 0); + wstart = std::max(wstart, 0); + hend = std::min(hend, rows_in); + wend = std::min(wend, cols_in); + + if(std::is_floating_point<T>::value) + { + for(int y = hstart; y < hend; ++y) + { + for(int x = wstart; x < wend; ++x) + { + avg_val += in[r * cols_in * rows_in + y * cols_in + x]; + } + } + out[r * rows_out * cols_out + i * pooled_width + k] = avg_val / pool; + } + else + { + static std::array<qint8_t, 10> scale_values_q8 = + { { 0x0, 0x0, 0x40, 0x2A, 0x20, 0x19, 0x15, 0x12, 0x10, 0xE } }; + + for(int y = hstart; y < hend; ++y) + { + for(int x = wstart; x < wend; ++x) + { + avg_val = sqadd_qs8(avg_val, in[r * cols_in * rows_in + y * cols_in + x]); + } + } + out[r * rows_out * cols_out + i * pooled_width + k] = sqmul_qs8(avg_val, (scale_values_q8[pool] >> (7 - fixed_point_position)), fixed_point_position); + } + } + } + } + } +} + +// Softmax Layer +template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type * = nullptr> +void softmax_layer(const Tensor<T> &in, Tensor<T> &out) +{ + const int cols = static_cast<int>(in.shape()[0]); + const int upper_dims = in.shape().total_size() / cols; + for(int r = 0; r < upper_dims; ++r) + { + // Find max + T max = std::numeric_limits<T>::lowest(); + for(int c = 0; c < cols; ++c) + { + const T x = in[r * cols + c]; + if(x > max) + { + max = x; + } + } + + // Regularize + T sum = 0; + for(int c = 0; c < cols; ++c) + { + const T res = exp(in[r * cols + c] - max); + out[r * cols + c] = res; + sum += res; + } + + // Normalize + const T norm_val = 1 / sum; + for(int c = 0; c < cols; ++c) + { + out[r * cols + c] *= norm_val; + } + } +} +template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type * = nullptr> +void softmax_layer(const Tensor<T> &in, Tensor<T> &out) +{ + using namespace fixed_point_arithmetic; + using promoted_T = typename test::traits::promote<T>::type; + + const int fixed_point_position = in.fixed_point_position(); + const int cols = static_cast<int>(in.shape()[0]); + const int upper_dims = in.shape().total_size() / cols; + + for(int r = 0; r < upper_dims; ++r) + { + // Find max + fixed_point<T> max(std::numeric_limits<T>::lowest(), fixed_point_position, true); + for(int c = 0; c < cols; ++c) + { + const fixed_point<T> x(in[r * cols + c], fixed_point_position, true); + if(x > max) + { + max = x; + } + } + + // Regularize + fixed_point<promoted_T> sum(0, fixed_point_position); + for(int c = 0; c < cols; ++c) + { + const fixed_point<T> x(in[r * cols + c], fixed_point_position, true); + fixed_point<T> res = exp(x - max); + out[r * cols + c] = res.raw(); + sum = add(sum, static_cast<fixed_point<promoted_T>>(res)); + } + + // Normalize + fixed_point<T> sat_sum(sum); + for(int c = 0; c < cols; ++c) + { + const fixed_point<T> x(out[r * cols + c], fixed_point_position, true); + out[r * cols + c] = div(x, sat_sum).raw(); + } + } +} + +// Fixed point operations +template <typename T> +void fixed_point_operation(const Tensor<T> &in, Tensor<T> &out, FixedPointOp op) +{ + int p = in.fixed_point_position(); + switch(op) + { + case FixedPointOp::EXP: + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = fixed_point_arithmetic::exp(fixed_point_arithmetic::fixed_point<T>(in[i], p, true)).raw(); + } + break; + case FixedPointOp::LOG: + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = fixed_point_arithmetic::log(fixed_point_arithmetic::fixed_point<T>(in[i], p, true)).raw(); + } + break; + case FixedPointOp::INV_SQRT: + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = fixed_point_arithmetic::inv_sqrt(fixed_point_arithmetic::fixed_point<T>(in[i], p, true)).raw(); + } + break; + case FixedPointOp::RECIPROCAL: + for(int i = 0; i < in.num_elements(); ++i) + { + out[i] = fixed_point_arithmetic::div(fixed_point_arithmetic::fixed_point<T>(1, p), fixed_point_arithmetic::fixed_point<T>(in[i], p, true)).raw(); + } + break; + default: + ARM_COMPUTE_ERROR("Fixed point operation not supported"); + break; + } +} + +// Tensor print +template <typename T> +void print(const Tensor<T> &in, std::ostream &out) +{ + out << "\n"; + for(int i = 0; i < in.num_elements(); ++i) + { + out << in[i] << " "; + } + out << "\n"; +} +} // namespace tensor_operations +} // namespace validation +} // namespace test +} // namespace arm_compute + +#endif /* __ARM_COMPUTE_TEST_TENSOR_OPERATIONS_H__ */ diff --git a/tests/validation/TensorVisitors.h b/tests/validation/TensorVisitors.h new file mode 100644 index 0000000000..a274140734 --- /dev/null +++ b/tests/validation/TensorVisitors.h @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_TENSOR_VISITORS_H__ +#define __ARM_COMPUTE_TEST_TENSOR_VISITORS_H__ + +#include "Tensor.h" +#include "TensorOperations.h" +#include "arm_compute/core/Error.h" + +#include "boost_wrapper.h" + +#include <ostream> + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace tensor_visitors +{ +// Absolute Difference visitor +struct absolute_difference_visitor : public boost::static_visitor<> +{ +public: + template <typename T1, typename T2, typename T3> + void operator()(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out) const + { + tensor_operations::absolute_difference(in1, in2, out); + } +}; +// Arithmetic Addition visitor +struct arithmetic_addition_visitor : public boost::static_visitor<> +{ +public: + explicit arithmetic_addition_visitor(ConvertPolicy convert_policy) + : _policy(convert_policy) + { + } + + template <typename T1, typename T2, typename T3> + void operator()(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out) const + { + tensor_operations::arithmetic_addition(in1, in2, out, _policy); + } + +private: + ConvertPolicy _policy; +}; +// Arithmetic Subtraction visitor +struct arithmetic_subtraction_visitor : public boost::static_visitor<> +{ +public: + explicit arithmetic_subtraction_visitor(ConvertPolicy convert_policy) + : _policy(convert_policy) + { + } + + template <typename T1, typename T2, typename T3> + void operator()(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out) const + { + tensor_operations::arithmetic_subtraction(in1, in2, out, _policy); + } + +private: + ConvertPolicy _policy; +}; +// Depth Convert visitor +struct depth_convert_visitor : public boost::static_visitor<> +{ +public: + explicit depth_convert_visitor(ConvertPolicy policy, uint32_t shift) + : _policy(policy), _shift(shift) + { + } + + template <typename T1, typename T2> + void operator()(const Tensor<T1> &in, Tensor<T2> &out) const + { + tensor_operations::depth_convert(in, out, _policy, _shift); + } + +private: + ConvertPolicy _policy; + uint32_t _shift; +}; +// GEMM visitor +struct gemm_visitor : public boost::static_visitor<> +{ +public: + explicit gemm_visitor(const TensorVariant &in1, const TensorVariant &in2, const TensorVariant &in3, float alpha, float beta) + : _in1(in1), _in2(in2), _in3(in3), _alpha(alpha), _beta(beta) + { + } + + template <typename T> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in1 = boost::get<Tensor<T>>(_in1); + const Tensor<T> &in2 = boost::get<Tensor<T>>(_in2); + const Tensor<T> &in3 = boost::get<Tensor<T>>(_in3); + tensor_operations::gemm(in1, in2, in3, out, _alpha, _beta); + } + +private: + const TensorVariant &_in1, &_in2, &_in3; + float _alpha; + float _beta; +}; +// Pixel-wise Multiplication visitor +struct pixel_wise_multiplication_visitor : public boost::static_visitor<> +{ +public: + explicit pixel_wise_multiplication_visitor(float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) + : _scale(scale), _convert_policy(convert_policy), _rounding_policy(rounding_policy) + { + } + + template <typename T1, typename T2, typename T3> + void operator()(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out) const + { + tensor_operations::pixel_wise_multiplication(in1, in2, out, _scale, _convert_policy, _rounding_policy); + } + +private: + float _scale; + ConvertPolicy _convert_policy; + RoundingPolicy _rounding_policy; +}; +// Fixed Point Pixel-wise Multiplication visitor +struct fixed_point_pixel_wise_multiplication_visitor : public boost::static_visitor<> +{ +public: + explicit fixed_point_pixel_wise_multiplication_visitor(const TensorVariant &in1, const TensorVariant &in2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) + : _in1(in1), _in2(in2), _scale(scale), _convert_policy(convert_policy), _rounding_policy(rounding_policy) + { + } + + template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in1 = boost::get<Tensor<T>>(_in1); + const Tensor<T> &in2 = boost::get<Tensor<T>>(_in2); + tensor_operations::fixed_point_pixel_wise_multiplication(in1, in2, out, _scale, _convert_policy, _rounding_policy); + } + template < typename T, typename std::enable_if < !std::is_integral<T>::value, int >::type = 0 > + void operator()(Tensor<T> &out) const + { + ARM_COMPUTE_ERROR("NOT SUPPORTED!"); + } + +private: + const TensorVariant &_in1; + const TensorVariant &_in2; + float _scale; + ConvertPolicy _convert_policy; + RoundingPolicy _rounding_policy; +}; +// Threshold operation +void threshold_operation(const Tensor<uint8_t> &in, Tensor<uint8_t> &out, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper) +{ + tensor_operations::threshold(in, out, threshold, false_value, true_value, type, upper); +} +// Activation layer visitor +struct activation_layer_visitor : public boost::static_visitor<> +{ +public: + explicit activation_layer_visitor(const TensorVariant &in, ActivationLayerInfo act_info) + : _in(in), _act_info(act_info) + { + } + + template <typename T> + void operator()(Tensor<T> &out) const + { + const auto &in = boost::get<Tensor<T>>(_in); + tensor_operations::activation_layer(in, out, _act_info); + } + +private: + const TensorVariant &_in; + const ActivationLayerInfo _act_info; +}; +// Batch Normalization Layer visitor +struct batch_normalization_layer_visitor : public boost::static_visitor<> +{ +public: + explicit batch_normalization_layer_visitor(const TensorVariant &in, const TensorVariant &mean, const TensorVariant &var, const TensorVariant &beta, const TensorVariant &gamma, float epsilon, + int fixed_point_position = 0) + : _in(in), _mean(mean), _var(var), _beta(beta), _gamma(gamma), _epsilon(epsilon), _fixed_point_position(fixed_point_position) + { + } + + template <typename T> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in = boost::get<Tensor<T>>(_in); + const Tensor<T> &mean = boost::get<Tensor<T>>(_mean); + const Tensor<T> &var = boost::get<Tensor<T>>(_var); + const Tensor<T> &beta = boost::get<Tensor<T>>(_beta); + const Tensor<T> &gamma = boost::get<Tensor<T>>(_gamma); + tensor_operations::batch_normalization_layer(in, out, mean, var, beta, gamma, _epsilon, _fixed_point_position); + } + +private: + const TensorVariant &_in, &_mean, &_var, &_beta, &_gamma; + float _epsilon; + int _fixed_point_position; +}; +// Convolution Layer visitor +struct convolution_layer_visitor : public boost::static_visitor<> +{ +public: + explicit convolution_layer_visitor(const TensorVariant &in, const TensorVariant &weights, const TensorVariant &bias, PadStrideInfo conv_info) + : _in(in), _weights(weights), _bias(bias), _conv_info(conv_info) + { + } + + template <typename T> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in = boost::get<Tensor<T>>(_in); + const Tensor<T> &weights = boost::get<Tensor<T>>(_weights); + const Tensor<T> &bias = boost::get<Tensor<T>>(_bias); + tensor_operations::convolution_layer(in, weights, bias, out, _conv_info); + } + +private: + const TensorVariant &_in; + const TensorVariant &_weights; + const TensorVariant &_bias; + PadStrideInfo _conv_info; +}; + +struct fully_connected_layer_visitor : public boost::static_visitor<> +{ +public: + explicit fully_connected_layer_visitor(const TensorVariant &in, const TensorVariant &weights, const TensorVariant &bias) + : _in(in), _weights(weights), _bias(bias) + { + } + template <typename T> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in = boost::get<Tensor<T>>(_in); + const Tensor<T> &weights = boost::get<Tensor<T>>(_weights); + const Tensor<T> &bias = boost::get<Tensor<T>>(_bias); + tensor_operations::fully_connected_layer(in, weights, bias, out); + } + +private: + const TensorVariant &_in; + const TensorVariant &_weights; + const TensorVariant &_bias; +}; + +// Normalization Layer visitor +struct normalization_layer_visitor : public boost::static_visitor<> +{ +public: + explicit normalization_layer_visitor(const TensorVariant &in, NormalizationLayerInfo norm_info) + : _in(in), _norm_info(norm_info) + { + } + + template <typename T> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in = boost::get<Tensor<T>>(_in); + tensor_operations::normalization_layer(in, out, _norm_info); + } + +private: + const TensorVariant &_in; + NormalizationLayerInfo _norm_info; +}; +// Pooling layer +struct pooling_layer_visitor : public boost::static_visitor<> +{ +public: + explicit pooling_layer_visitor(const TensorVariant &in, PoolingLayerInfo pool_info, int fixed_point_position = 0) + : _in(in), _pool_info(pool_info), _fixed_point_position(fixed_point_position) + { + } + + template <typename T> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in = boost::get<Tensor<T>>(_in); + tensor_operations::pooling_layer(in, out, _pool_info, _fixed_point_position); + } + +private: + const TensorVariant &_in; + PoolingLayerInfo _pool_info; + int _fixed_point_position; +}; +// Softmax Layer visitor +struct softmax_layer_visitor : public boost::static_visitor<> +{ +public: + explicit softmax_layer_visitor(const TensorVariant &in) + : _in(in) + { + } + + template <typename T> + void operator()(Tensor<T> &out) const + { + const auto &in = boost::get<Tensor<T>>(_in); + tensor_operations::softmax_layer(in, out); + } + +private: + const TensorVariant &_in; +}; +// Fixed Point operations visitor +struct fixed_point_operation_visitor : public boost::static_visitor<> +{ +public: + explicit fixed_point_operation_visitor(const TensorVariant &in, FixedPointOp op) + : _in(in), _op(op) + { + } + + template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in = boost::get<Tensor<T>>(_in); + tensor_operations::fixed_point_operation(in, out, _op); + } + template < typename T, typename std::enable_if < !std::is_integral<T>::value, int >::type = 0 > + void operator()(Tensor<T> &out) const + { + ARM_COMPUTE_ERROR("NOT SUPPORTED!"); + } + +private: + const TensorVariant &_in; + FixedPointOp _op; +}; +// Print Tensor visitor +struct print_visitor : public boost::static_visitor<> +{ +public: + explicit print_visitor(std::ostream &out) + : _out(out) + { + } + + template <typename T> + void operator()(const Tensor<T> &in) const + { + tensor_operations::print(in, _out); + } + +private: + std::ostream &_out; +}; +} // namespace tensor_visitors +} // namespace validation +} // namespace test +} // namespace arm_compute + +#endif /* __ARM_COMPUTE_TEST_TENSOR_VISITORS_H__ */ diff --git a/tests/validation/UNIT/CMakeLists.txt b/tests/validation/UNIT/CMakeLists.txt new file mode 100644 index 0000000000..a0603f150c --- /dev/null +++ b/tests/validation/UNIT/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (c) 2017 ARM Limited. +# +# SPDX-License-Identifier: MIT +# +# 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. +cmake_minimum_required (VERSION 3.1) + +set(arm_compute_test_validation_UNIT_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/TensorInfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Utils.cpp +) + +add_library(arm_compute_test_validation_UNIT OBJECT + ${arm_compute_test_validation_UNIT_SOURCE_FILES} +) + +set(arm_compute_test_validation_TARGET_OBJECTS + ${arm_compute_test_validation_TARGET_OBJECTS} + $<TARGET_OBJECTS:arm_compute_test_validation_UNIT> + PARENT_SCOPE +) diff --git a/tests/validation/UNIT/FixedPoint.cpp b/tests/validation/UNIT/FixedPoint.cpp new file mode 100644 index 0000000000..63d4150318 --- /dev/null +++ b/tests/validation/UNIT/FixedPoint.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "validation/FixedPoint.h" + +#include "TypePrinter.h" +#include "Utils.h" +#include "validation/Validation.h" +#include "validation/ValidationUserConfiguration.h" + +#include "boost_wrapper.h" + +#include <fstream> +#include <vector> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::validation; + +namespace +{ +std::string func_names[] = +{ + "add", "sub", "mul", "exp", "log", "inv_sqrt" +}; +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(UNIT) +BOOST_AUTO_TEST_SUITE(FixedPoint) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(FixedPointQS8Inputs, boost::unit_test::data::make(func_names) * boost::unit_test::data::xrange(1, 7), func_name, frac_bits) +{ + const std::string base_file_name = user_config.path.get() + "/dumps/" + func_name + "_Q8." + cpp11::to_string(frac_bits); + std::ifstream inputs_file{ base_file_name + ".in", std::ios::binary | std::ios::in }; + + BOOST_TEST_INFO(base_file_name + ".in"); + BOOST_TEST_REQUIRE(inputs_file.good()); + + float float_val = 0.f; + + // Read first value + inputs_file.read(reinterpret_cast<char *>(&float_val), sizeof(float_val)); + + while(inputs_file.good()) + { + // Convert to fixed point + fixed_point_arithmetic::fixed_point<int8_t> in_val(float_val, frac_bits); + + // Check that the value didn't change + BOOST_TEST(static_cast<float>(in_val) == float_val); + + // Read next value + inputs_file.read(reinterpret_cast<char *>(&float_val), sizeof(float_val)); + } +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +//FIXME: Figure out how to handle expected failures properly +// The last input argument specifies the expected number of failures for a +// given combination of (function name, number of fractional bits) as defined +// by the first two arguments. +BOOST_DATA_TEST_CASE(FixedPointQS8Outputs, (boost::unit_test::data::make(func_names) * boost::unit_test::data::xrange(1, 7)) ^ (boost::unit_test::data::make({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 13, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 32, 67 })), + func_name, frac_bits, expected_failures) +{ + const std::string base_file_name = user_config.path.get() + "/dumps/" + func_name + "_Q8." + cpp11::to_string(frac_bits); + std::ifstream inputs_file{ base_file_name + ".in", std::ios::binary | std::ios::in }; + std::ifstream reference_file{ base_file_name + ".out", std::ios::binary | std::ios::in }; + + BOOST_TEST_INFO(base_file_name + ".in"); + BOOST_TEST_REQUIRE(inputs_file.good()); + BOOST_TEST_INFO(base_file_name + ".out"); + BOOST_TEST_REQUIRE(reference_file.good()); + + const float step_size = std::pow(2.f, -frac_bits); + + float float_val = 0.f; + float ref_val = 0.f; + int64_t num_mismatches = 0; + + // Read first values + inputs_file.read(reinterpret_cast<char *>(&float_val), sizeof(float_val)); + reference_file.read(reinterpret_cast<char *>(&ref_val), sizeof(ref_val)); + + while(inputs_file.good() && reference_file.good()) + { + fixed_point_arithmetic::fixed_point<int8_t> in_val(float_val, frac_bits); + fixed_point_arithmetic::fixed_point<int8_t> out_val(0.f, frac_bits); + + float tolerance = 0.f; + + if(func_name == "add") + { + out_val = in_val + in_val; + } + else if(func_name == "sub") + { + out_val = in_val - in_val; //NOLINT + } + else if(func_name == "mul") + { + tolerance = 1.f * step_size; + out_val = in_val * in_val; + } + else if(func_name == "exp") + { + tolerance = 2.f * step_size; + out_val = fixed_point_arithmetic::exp(in_val); + } + else if(func_name == "log") + { + tolerance = 4.f * step_size; + out_val = fixed_point_arithmetic::log(in_val); + } + else if(func_name == "inv_sqrt") + { + tolerance = 5.f * step_size; + out_val = fixed_point_arithmetic::inv_sqrt(in_val); + } + + BOOST_TEST_INFO("input = " << in_val); + BOOST_TEST_INFO("output = " << out_val); + BOOST_TEST_INFO("reference = " << ref_val); + BOOST_TEST_INFO("tolerance = " << tolerance); + BOOST_TEST_WARN((std::abs(static_cast<float>(out_val) - ref_val) <= tolerance)); + + if(std::abs(static_cast<float>(out_val) - ref_val) > tolerance) + { + ++num_mismatches; + } + + // Read next values + inputs_file.read(reinterpret_cast<char *>(&float_val), sizeof(float_val)); + reference_file.read(reinterpret_cast<char *>(&ref_val), sizeof(ref_val)); + } + + BOOST_TEST(num_mismatches == expected_failures); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/UNIT/TensorInfo.cpp b/tests/validation/UNIT/TensorInfo.cpp new file mode 100644 index 0000000000..11ed9f6dcc --- /dev/null +++ b/tests/validation/UNIT/TensorInfo.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "TypePrinter.h" +#include "validation/Validation.h" + +#include "arm_compute/core/TensorInfo.h" +#include "arm_compute/core/Types.h" + +#include "boost_wrapper.h" + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::validation; + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(UNIT) +BOOST_AUTO_TEST_SUITE(TensorInfoValidation) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(AutoPadding, + boost::unit_test::data::make({ TensorShape{}, + TensorShape{ 10U }, + TensorShape{ 10U, 10U }, + TensorShape{ 10U, 10U, 10U }, + TensorShape{ 10U, 10U, 10U, 10U }, + TensorShape{ 10U, 10U, 10U, 10U, 10U }, + TensorShape{ 10U, 10U, 10U, 10U, 10U, 10U } + }) + ^ boost::unit_test::data::make({ PaddingSize{ 0, 0, 0, 0 }, + PaddingSize{ 0, 36, 0, 4 }, + PaddingSize{ 4, 36, 4, 4 }, + PaddingSize{ 4, 36, 4, 4 }, + PaddingSize{ 4, 36, 4, 4 }, + PaddingSize{ 4, 36, 4, 4 }, + PaddingSize{ 4, 36, 4, 4 } + }) + ^ boost::unit_test::data::make({ Strides{}, + Strides{ 1U }, + Strides{ 1U, 50U }, + Strides{ 1U, 50U, 900U }, + Strides{ 1U, 50U, 900U, 9000U }, + Strides{ 1U, 50U, 900U, 9000U, 90000U }, + Strides{ 1U, 50U, 900U, 9000U, 90000U, 900000U } + }) + ^ boost::unit_test::data::make( +{ + 0, + 4, + 204, + 204, + 204, + 204, + 204, +}), +shape, auto_padding, strides, offset) +{ + TensorInfo info{ shape, Format::U8 }; + + BOOST_TEST(!info.has_padding()); + + info.auto_padding(); + + validate(info.padding(), auto_padding); + BOOST_TEST(compare_dimensions(info.strides_in_bytes(), strides)); + BOOST_TEST(info.offset_first_element_in_bytes() == offset); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/UNIT/TensorShape.cpp b/tests/validation/UNIT/TensorShape.cpp new file mode 100644 index 0000000000..2d78cd549a --- /dev/null +++ b/tests/validation/UNIT/TensorShape.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "TypePrinter.h" +#include "validation/Validation.h" + +#include "arm_compute/core/TensorShape.h" + +#include "boost_wrapper.h" + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::validation; + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(UNIT) +BOOST_AUTO_TEST_SUITE(TensorShapeValidation) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Construction, + boost::unit_test::data::make({ TensorShape{}, + TensorShape{ 1U }, + TensorShape{ 2U }, + TensorShape{ 2U, 3U }, + TensorShape{ 2U, 3U, 5U }, + TensorShape{ 2U, 3U, 5U, 7U }, + TensorShape{ 2U, 3U, 5U, 7U, 11U }, + TensorShape{ 2U, 3U, 5U, 7U, 11U, 13U } + }) + ^ boost::unit_test::data::make({ 0, 0, 1, 2, 3, 4, 5, 6 }) ^ boost::unit_test::data::make({ 0, 1, 2, 6, 30, 210, 2310, 30030 }), + shape, num_dimensions, total_size) +{ + BOOST_TEST(shape.num_dimensions() == num_dimensions); + BOOST_TEST(shape.total_size() == total_size); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(SetEmpty, boost::unit_test::data::make({ 0, 1, 2, 3, 4, 5 }), dimension) +{ + TensorShape shape; + + shape.set(dimension, 10); + + BOOST_TEST(shape.num_dimensions() == dimension + 1); + BOOST_TEST(shape.total_size() == 10); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/UNIT/Utils.cpp b/tests/validation/UNIT/Utils.cpp new file mode 100644 index 0000000000..7a09be52b5 --- /dev/null +++ b/tests/validation/UNIT/Utils.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Utils.h" + +#include "TypePrinter.h" +#include "validation/Validation.h" + +#include "boost_wrapper.h" + +#include <stdexcept> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cpp11; +using namespace arm_compute::test::validation; + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(UNIT) +BOOST_AUTO_TEST_SUITE(Utils) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RoundHalfUp, boost::unit_test::data::make({ 1.f, 1.2f, 1.5f, 2.5f, 2.9f, -3.f, -3.5f, -3.8f, -4.3f, -4.5f }) ^ boost::unit_test::data::make({ 1.f, 1.f, 2.f, 3.f, 3.f, -3.f, -3.f, -4.f, -4.f, -4.f }), + value, result) +{ + BOOST_TEST(cpp11::round_half_up(value) == result); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(RoundHalfEven, boost::unit_test::data::make({ 1.f, 1.2f, 1.5f, 2.5f, 2.9f, -3.f, -3.5f, -3.8f, -4.3f, -4.5f }) ^ boost::unit_test::data::make({ 1.f, 1.f, 2.f, 2.f, 3.f, -3.f, -4.f, -4.f, -4.f, -4.f }), + value, result) +{ + BOOST_TEST(cpp11::round_half_even(value) == result); +} + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Index2Coord, boost::unit_test::data::make({ TensorShape{ 1U }, TensorShape{ 2U }, TensorShape{ 2U, 3U } }) ^ boost::unit_test::data::make({ 0, 1, 2 }) ^ + boost::unit_test::data::make({ Coordinates{ 0 }, Coordinates{ 1 }, Coordinates{ 0, 1 } }), shape, index, ref_coordinate) +{ + Coordinates coordinate = index2coord(shape, index); + + BOOST_TEST(compare_dimensions(coordinate, ref_coordinate)); +} + +//FIXME: Negative tests only work in debug mode +#if 0 +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Index2CoordFail, boost::unit_test::data::make({ TensorShape{}, TensorShape{ 2U }, TensorShape{ 2U } }) ^ boost::unit_test::data::make({ 0, -1, 2 }), shape, index) +{ + BOOST_CHECK_THROW(index2coord(shape, index), std::runtime_error); +} +#endif + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Coord2Index, boost::unit_test::data::make({ TensorShape{ 1U }, TensorShape{ 2U }, TensorShape{ 2U, 3U } }) ^ boost::unit_test::data::make({ Coordinates{ 0 }, Coordinates{ 1 }, Coordinates{ 0, 1 } }) + ^ boost::unit_test::data::make({ 0, 1, 2 }), + shape, coordinate, ref_index) +{ + int index = coord2index(shape, coordinate); + + BOOST_TEST(index == ref_index); +} + +//FIXME: Negative tests only work in debug mode +#if 0 +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly")) +BOOST_DATA_TEST_CASE(Coord2IndexFail, boost::unit_test::data::make({ TensorShape{}, TensorShape{ 2U } }) ^ boost::unit_test::data::make({ Coordinates{ 0 }, Coordinates{} }), shape, coordinate) +{ + BOOST_CHECK_THROW(coord2index(shape, coordinate), std::runtime_error); +} +#endif + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/Validation.cpp b/tests/validation/Validation.cpp new file mode 100644 index 0000000000..335d2644d3 --- /dev/null +++ b/tests/validation/Validation.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "Validation.h" + +#include "IAccessor.h" +#include "RawTensor.h" +#include "TypePrinter.h" +#include "Utils.h" + +#include "arm_compute/core/Coordinates.h" +#include "arm_compute/core/Error.h" +#include "arm_compute/core/FixedPoint.h" +#include "arm_compute/core/TensorShape.h" +#include "arm_compute/runtime/Tensor.h" + +#include <array> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <iomanip> + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace +{ +/** Get the data from *ptr after casting according to @p data_type and then convert the data to double. + * + * @param[in] ptr Pointer to value. + * @param[in] data_type Data type of both values. + * + * @return The data from the ptr after converted to double. + */ +double get_double_data(const void *ptr, DataType data_type) +{ + switch(data_type) + { + case DataType::U8: + return *reinterpret_cast<const uint8_t *>(ptr); + case DataType::S8: + return *reinterpret_cast<const int8_t *>(ptr); + case DataType::QS8: + return *reinterpret_cast<const qint8_t *>(ptr); + case DataType::U16: + return *reinterpret_cast<const uint16_t *>(ptr); + case DataType::S16: + return *reinterpret_cast<const int16_t *>(ptr); + case DataType::U32: + return *reinterpret_cast<const uint32_t *>(ptr); + case DataType::S32: + return *reinterpret_cast<const int32_t *>(ptr); + case DataType::U64: + return *reinterpret_cast<const uint64_t *>(ptr); + case DataType::S64: + return *reinterpret_cast<const int64_t *>(ptr); +#if ENABLE_FP16 + case DataType::F16: + return *reinterpret_cast<const float16_t *>(ptr); +#endif + case DataType::F32: + return *reinterpret_cast<const float *>(ptr); + case DataType::F64: + return *reinterpret_cast<const double *>(ptr); + case DataType::SIZET: + return *reinterpret_cast<const size_t *>(ptr); + default: + ARM_COMPUTE_ERROR("NOT SUPPORTED!"); + } +} + +void check_border_element(const IAccessor &tensor, const Coordinates &id, + const BorderMode &border_mode, const void *border_value, + int64_t &num_elements, int64_t &num_mismatches) +{ + const size_t channel_size = element_size_from_data_type(tensor.data_type()); + const auto ptr = static_cast<const uint8_t *>(tensor(id)); + + if(border_mode == BorderMode::REPLICATE) + { + Coordinates border_id{ id }; + border_id.set(1, 0); + border_value = tensor(border_id); + } + + // Iterate over all channels within one element + for(int channel = 0; channel < tensor.num_channels(); ++channel) + { + const size_t channel_offset = channel * channel_size; + const double target = get_double_data(ptr + channel_offset, tensor.data_type()); + const double ref = get_double_data(static_cast<const uint8_t *>(border_value) + channel_offset, tensor.data_type()); + const double difference = target - ref; + + BOOST_TEST_INFO("id = " << id); + BOOST_TEST_INFO("channel = " << channel); + BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref); + BOOST_TEST_INFO("target = " << std::setprecision(5) << target); + BOOST_TEST_WARN(difference == 0); + + if(difference != 0.f) + { + ++num_mismatches; + } + + ++num_elements; + } +} + +void check_single_element(const Coordinates &id, const IAccessor &tensor, const RawTensor &reference, float tolerance_value, + uint64_t wrap_range, int min_channels, size_t channel_size, int64_t &num_mismatches, int64_t &num_elements) +{ + const auto ptr = static_cast<const uint8_t *>(tensor(id)); + const auto ref_ptr = static_cast<const uint8_t *>(reference(id)); + + // Iterate over all channels within one element + for(int channel = 0; channel < min_channels; ++channel) + { + const size_t channel_offset = channel * channel_size; + const double target = get_double_data(ptr + channel_offset, reference.data_type()); + const double ref = get_double_data(ref_ptr + channel_offset, reference.data_type()); + const double difference = target - ref; + + BOOST_TEST_INFO("id = " << id); + BOOST_TEST_INFO("channel = " << channel); + BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref); + BOOST_TEST_INFO("target = " << std::setprecision(5) << target); + BOOST_TEST_WARN(difference == 0); + + if(std::abs(difference) > tolerance_value) + { + // If no special cases for tolerating wrappping cases + // or the special case of wrapping exceeds tolerance_value + if(wrap_range == 0 || (wrap_range - std::abs(difference)) > tolerance_value) + { + ++num_mismatches; + } + } + ++num_elements; + } +} +} // namespace + +void validate(const arm_compute::ValidRegion ®ion, const arm_compute::ValidRegion &reference) +{ + BOOST_TEST(region.anchor.num_dimensions() == reference.anchor.num_dimensions()); + BOOST_TEST(region.shape.num_dimensions() == reference.shape.num_dimensions()); + + for(unsigned int d = 0; d < region.anchor.num_dimensions(); ++d) + { + BOOST_TEST(region.anchor[d] == reference.anchor[d]); + } + + for(unsigned int d = 0; d < region.shape.num_dimensions(); ++d) + { + BOOST_TEST(region.shape[d] == reference.shape[d]); + } +} + +void validate(const arm_compute::PaddingSize &padding, const arm_compute::PaddingSize &reference) +{ + BOOST_TEST(padding.top == reference.top); + BOOST_TEST(padding.right == reference.right); + BOOST_TEST(padding.bottom == reference.bottom); + BOOST_TEST(padding.left == reference.left); +} + +void validate(const IAccessor &tensor, const RawTensor &reference, float tolerance_value, float tolerance_number, uint64_t wrap_range) +{ + // Validate with valid region covering the entire shape + validate(tensor, reference, shape_to_valid_region(tensor.shape()), tolerance_value, tolerance_number, wrap_range); +} + +void validate(const IAccessor &tensor, const RawTensor &reference, const ValidRegion &valid_region, float tolerance_value, float tolerance_number, uint64_t wrap_range) +{ + int64_t num_mismatches = 0; + int64_t num_elements = 0; + + BOOST_TEST(tensor.element_size() == reference.element_size()); + BOOST_TEST(tensor.format() == reference.format()); + BOOST_TEST(tensor.data_type() == reference.data_type()); + BOOST_TEST(tensor.num_channels() == reference.num_channels()); + BOOST_TEST(compare_dimensions(tensor.shape(), reference.shape())); + + const int min_elements = std::min(tensor.num_elements(), reference.num_elements()); + const int min_channels = std::min(tensor.num_channels(), reference.num_channels()); + const size_t channel_size = element_size_from_data_type(reference.data_type()); + + // Iterate over all elements within valid region, e.g. U8, S16, RGB888, ... + for(int element_idx = 0; element_idx < min_elements; ++element_idx) + { + const Coordinates id = index2coord(reference.shape(), element_idx); + if(is_in_valid_region(valid_region, id)) + { + check_single_element(id, tensor, reference, tolerance_value, wrap_range, min_channels, channel_size, num_mismatches, num_elements); + } + } + + const int64_t absolute_tolerance_number = tolerance_number * num_elements; + const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f; + + BOOST_TEST(num_mismatches <= absolute_tolerance_number, + num_mismatches << " values (" << std::setprecision(2) << percent_mismatches + << "%) mismatched (maximum tolerated " << std::setprecision(2) << tolerance_number << "%)"); +} + +void validate(const IAccessor &tensor, const void *reference_value) +{ + BOOST_TEST_REQUIRE((reference_value != nullptr)); + + int64_t num_mismatches = 0; + int64_t num_elements = 0; + const size_t channel_size = element_size_from_data_type(tensor.data_type()); + + // Iterate over all elements, e.g. U8, S16, RGB888, ... + for(int element_idx = 0; element_idx < tensor.num_elements(); ++element_idx) + { + const Coordinates id = index2coord(tensor.shape(), element_idx); + + const auto ptr = static_cast<const uint8_t *>(tensor(id)); + + // Iterate over all channels within one element + for(int channel = 0; channel < tensor.num_channels(); ++channel) + { + const size_t channel_offset = channel * channel_size; + const double target = get_double_data(ptr + channel_offset, tensor.data_type()); + const double ref = get_double_data(reference_value, tensor.data_type()); + const double difference = target - ref; + + BOOST_TEST_INFO("id = " << id); + BOOST_TEST_INFO("channel = " << channel); + BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref); + BOOST_TEST_INFO("target = " << std::setprecision(5) << target); + BOOST_TEST_WARN(difference == 0); + + if(difference != 0.f) + { + ++num_mismatches; + } + + ++num_elements; + } + } + + const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f; + + BOOST_TEST(num_mismatches == 0, + num_mismatches << " values (" << std::setprecision(2) << percent_mismatches << "%) mismatched"); +} + +void validate(const IAccessor &tensor, BorderSize border_size, const BorderMode &border_mode, const void *border_value) +{ + if(border_mode == BorderMode::UNDEFINED) + { + return; + } + else if(border_mode == BorderMode::CONSTANT) + { + BOOST_TEST((border_value != nullptr)); + } + + int64_t num_mismatches = 0; + int64_t num_elements = 0; + const int slice_size = tensor.shape()[0] * tensor.shape()[1]; + + for(int element_idx = 0; element_idx < tensor.num_elements(); element_idx += slice_size) + { + Coordinates id = index2coord(tensor.shape(), element_idx); + + // Top border + for(int y = -border_size.top; y < 0; ++y) + { + id.set(1, y); + + for(int x = -border_size.left; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x) + { + id.set(0, x); + + check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches); + } + } + + // Bottom border + for(int y = tensor.shape()[1]; y < static_cast<int>(tensor.shape()[1]) + static_cast<int>(border_size.bottom); ++y) + { + id.set(1, y); + + for(int x = -border_size.left; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x) + { + id.set(0, x); + + check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches); + } + } + + // Left/right border + for(int y = 0; y < static_cast<int>(tensor.shape()[1]); ++y) + { + id.set(1, y); + + // Left border + for(int x = -border_size.left; x < 0; ++x) + { + id.set(0, x); + + check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches); + } + + // Right border + for(int x = tensor.shape()[0]; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x) + { + id.set(0, x); + + check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches); + } + } + } + + const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f; + + BOOST_TEST(num_mismatches == 0, + num_mismatches << " values (" << std::setprecision(2) << percent_mismatches << "%) mismatched"); +} + +void validate(std::vector<unsigned int> classified_labels, std::vector<unsigned int> expected_labels) +{ + BOOST_TEST(expected_labels.size() != 0); + BOOST_TEST(classified_labels.size() == expected_labels.size()); + + for(unsigned int i = 0; i < expected_labels.size(); ++i) + { + BOOST_TEST(classified_labels[i] == expected_labels[i]); + } +} +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/Validation.h b/tests/validation/Validation.h new file mode 100644 index 0000000000..865d05b1f6 --- /dev/null +++ b/tests/validation/Validation.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_REFERENCE_VALIDATION_H__ +#define __ARM_COMPUTE_TEST_REFERENCE_VALIDATION_H__ + +#include "arm_compute/core/Types.h" + +#include "boost_wrapper.h" + +#include <vector> + +namespace arm_compute +{ +class Tensor; + +namespace test +{ +class RawTensor; +class IAccessor; + +namespace validation +{ +template <typename T> +boost::test_tools::predicate_result compare_dimensions(const Dimensions<T> &dimensions1, const Dimensions<T> &dimensions2) +{ + if(dimensions1.num_dimensions() != dimensions2.num_dimensions()) + { + boost::test_tools::predicate_result result(false); + result.message() << "Different dimensionality [" << dimensions1.num_dimensions() << "!=" << dimensions2.num_dimensions() << "]"; + return result; + } + + for(unsigned int i = 0; i < dimensions1.num_dimensions(); ++i) + { + if(dimensions1[i] != dimensions2[i]) + { + boost::test_tools::predicate_result result(false); + result.message() << "Mismatch in dimension " << i << " [" << dimensions1[i] << "!=" << dimensions2[i] << "]"; + return result; + } + } + + return true; +} + +/** Validate valid regions. + * + * - Dimensionality has to be the same. + * - Anchors have to match. + * - Shapes have to match. + */ +void validate(const arm_compute::ValidRegion ®ion, const arm_compute::ValidRegion &reference); + +/** Validate padding. + * + * Padding on all sides has to be the same. + */ +void validate(const arm_compute::PaddingSize &padding, const arm_compute::PaddingSize &reference); + +/** Validate tensors. + * + * - Dimensionality has to be the same. + * - All values have to match. + * + * @note: wrap_range allows cases where reference tensor rounds up to the wrapping point, causing it to wrap around to + * zero while the test tensor stays at wrapping point to pass. This may permit true erroneous cases (difference between + * reference tensor and test tensor is multiple of wrap_range), but such errors would be detected by + * other test cases. + */ +void validate(const IAccessor &tensor, const RawTensor &reference, float tolerance_value = 0.f, float tolerance_number = 0.f, uint64_t wrap_range = 0); + +/** Validate tensors with valid region. + * + * - Dimensionality has to be the same. + * - All values have to match. + * + * @note: wrap_range allows cases where reference tensor rounds up to the wrapping point, causing it to wrap around to + * zero while the test tensor stays at wrapping point to pass. This may permit true erroneous cases (difference between + * reference tensor and test tensor is multiple of wrap_range), but such errors would be detected by + * other test cases. + */ +void validate(const IAccessor &tensor, const RawTensor &reference, const ValidRegion &valid_region, float tolerance_value = 0.f, float tolerance_number = 0.f, uint64_t wrap_range = 0); + +/** Validate tensors against constant value. + * + * - All values have to match. + */ +void validate(const IAccessor &tensor, const void *reference_value); + +/** Validate border against a constant value. + * + * - All border values have to match the specified value if mode is CONSTANT. + * - All border values have to be replicated if mode is REPLICATE. + * - Nothing is validated for mode UNDEFINED. + */ +void validate(const IAccessor &tensor, BorderSize border_size, const BorderMode &border_mode, const void *border_value); + +/** Validate classified labels against expected ones. + * + * - All values should match + */ +void validate(std::vector<unsigned int> classified_labels, std::vector<unsigned int> expected_labels); +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif diff --git a/tests/validation/ValidationProgramOptions.cpp b/tests/validation/ValidationProgramOptions.cpp new file mode 100644 index 0000000000..adb8c5ab6c --- /dev/null +++ b/tests/validation/ValidationProgramOptions.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "ValidationProgramOptions.h" + +#include <thread> + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#pragma GCC diagnostic ignored "-Wctor-dtor-privacy" +#include "boost/program_options.hpp" +#pragma GCC diagnostic pop + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +ValidationProgramOptions::ValidationProgramOptions() +{ + boost::program_options::options_description options("Validation options"); + options.add_options()("runs", boost::program_options::value<unsigned int>()->default_value(1), "Repetitions per test"); + options.add_options()("threads", boost::program_options::value<unsigned int>()->default_value(std::thread::hardware_concurrency()), "Number of parallel CPU threads"); + add_options(options); +} +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/ValidationProgramOptions.h b/tests/validation/ValidationProgramOptions.h new file mode 100644 index 0000000000..bf30db960d --- /dev/null +++ b/tests/validation/ValidationProgramOptions.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_VALIDATION_PROGRAM_OPTIONS_H__ +#define __ARM_COMPUTE_TEST_VALIDATION_PROGRAM_OPTIONS_H__ + +#include "ProgramOptions.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +/** Subclass of @ref ProgramOptions that adds validation specific options. */ +class ValidationProgramOptions : public ProgramOptions +{ +public: + /** Defines additonal options. */ + ValidationProgramOptions(); +}; +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif diff --git a/tests/validation/ValidationUserConfiguration.h b/tests/validation/ValidationUserConfiguration.h new file mode 100644 index 0000000000..28b58e8375 --- /dev/null +++ b/tests/validation/ValidationUserConfiguration.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 __ARM_COMPUTE_TEST_REFERENCE_VALIDATION_USER_CONFIGURATION_H__ +#define __ARM_COMPUTE_TEST_REFERENCE_VALIDATION_USER_CONFIGURATION_H__ + +#include "UserConfiguration.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +// Validation requires no specific configuration +using ValidationUserConfiguration = UserConfiguration; +} // namespace validation + +extern validation::ValidationUserConfiguration user_config; +} // namespace test +} // namespace arm_compute +#endif diff --git a/tests/validation/main.cpp b/tests/validation/main.cpp new file mode 100644 index 0000000000..844ee36200 --- /dev/null +++ b/tests/validation/main.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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. + */ +#define BOOST_TEST_ALTERNATIVE_INIT_API + +#include "Globals.h" +#include "TensorLibrary.h" +#include "Utils.h" +#include "ValidationProgramOptions.h" +#include "ValidationUserConfiguration.h" + +#include "arm_compute/runtime/Scheduler.h" + +#include "boost_wrapper.h" + +#include <iostream> +#include <memory> +#include <random> + +using namespace arm_compute::test; +using namespace arm_compute::test::validation; + +namespace arm_compute +{ +namespace test +{ +ValidationUserConfiguration user_config; +std::unique_ptr<TensorLibrary> library; +} // namespace test +} // namespace arm_compute + +struct GlobalFixture +{ + GlobalFixture() + { + if(user_config.seed.is_set()) + { + library = cpp14::make_unique<TensorLibrary>(user_config.path.get(), user_config.seed); + } + else + { + library = cpp14::make_unique<TensorLibrary>(user_config.path.get()); + } + + BOOST_TEST_MESSAGE("Seed: " << library->seed()); + } +}; + +BOOST_GLOBAL_FIXTURE(GlobalFixture); + +bool init_unit_test() +{ + boost::unit_test::framework::master_test_suite().p_name.value = "Compute Library Validation Tests"; + + ValidationProgramOptions options; + + int &argc = boost::unit_test::framework::master_test_suite().argc; + char **argv = boost::unit_test::framework::master_test_suite().argv; + + try + { + options.parse_commandline(argc, argv); + + if(options.wants_help()) + { + std::cout << "Usage: " << argv[0] << " [options] PATH\n"; + std::cout << options.get_help() << "\n"; + return false; + } + + user_config = ValidationUserConfiguration(options); + } + catch(const boost::program_options::required_option &err) + { + std::cerr << "Error: " << err.what() << "\n"; + std::cout << "\nUsage: " << argv[0] << " [options] PATH\n"; + std::cout << options.get_help() << "\n"; + return false; + } + + std::cout << "Using " << user_config.threads << " CPU " << (user_config.threads == 1 ? "thread" : "threads") << "\n"; + arm_compute::Scheduler::get().set_num_threads(user_config.threads); + return true; +} diff --git a/tests/validation/system_tests/CL/AlexNet.cpp b/tests/validation/system_tests/CL/AlexNet.cpp new file mode 100644 index 0000000000..f7a88207c4 --- /dev/null +++ b/tests/validation/system_tests/CL/AlexNet.cpp @@ -0,0 +1,111 @@ +#ifdef INTERNAL_ONLY //FIXME Delete this file before the release +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "CL/CLAccessor.h" +#include "CL/Helper.h" +#include "validation/Validation.h" + +#include "arm_compute/runtime/CL/CLSubTensor.h" +#include "arm_compute/runtime/CL/functions/CLActivationLayer.h" +#include "arm_compute/runtime/CL/functions/CLConvolutionLayer.h" +#include "arm_compute/runtime/CL/functions/CLFullyConnectedLayer.h" +#include "arm_compute/runtime/CL/functions/CLNormalizationLayer.h" +#include "arm_compute/runtime/CL/functions/CLPoolingLayer.h" +#include "arm_compute/runtime/CL/functions/CLSoftmaxLayer.h" + +#include "model_objects/AlexNet.h" + +#include <array> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cl; +using namespace arm_compute::test::validation; + +namespace +{ +using CLAlexNetModel = model_objects::AlexNet<ICLTensor, + CLTensor, + CLSubTensor, + CLAccessor, + CLActivationLayer, + CLConvolutionLayer, + CLFullyConnectedLayer, + CLNormalizationLayer, + CLPoolingLayer, + CLSoftmaxLayer>; +std::vector<unsigned int> compute_alexnet(unsigned int batches, std::string input_file) +{ + std::vector<std::string> weight_files = { "cnn_data/alexnet_model/conv1_w.dat", + "cnn_data/alexnet_model/conv2_w.dat", + "cnn_data/alexnet_model/conv3_w.dat", + "cnn_data/alexnet_model/conv4_w.dat", + "cnn_data/alexnet_model/conv5_w.dat", + "cnn_data/alexnet_model/fc6_w.dat", + "cnn_data/alexnet_model/fc7_w.dat", + "cnn_data/alexnet_model/fc8_w.dat" + }; + + std::vector<std::string> bias_files = { "cnn_data/alexnet_model/conv1_b.dat", + "cnn_data/alexnet_model/conv2_b.dat", + "cnn_data/alexnet_model/conv3_b.dat", + "cnn_data/alexnet_model/conv4_b.dat", + "cnn_data/alexnet_model/conv5_b.dat", + "cnn_data/alexnet_model/fc6_b.dat", + "cnn_data/alexnet_model/fc7_b.dat", + "cnn_data/alexnet_model/fc8_b.dat" + }; + CLAlexNetModel network{}; + network.init_weights(batches); + network.build(); + network.allocate(); + network.fill(weight_files, bias_files); + network.feed(std::move(input_file)); + network.run(); + + return network.get_classifications(); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(SYSTEM_TESTS) +BOOST_AUTO_TEST_SUITE(CL) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_AUTO_TEST_CASE(AlexNet) +{ + // Compute alexnet + std::vector<unsigned int> classified_labels = compute_alexnet(1, "cnn_data/imagenet_data/shark.dat"); + + // Expected labels + std::vector<unsigned int> expected_labels = { 2 }; + + // Validate labels + validate(classified_labels, expected_labels); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif +#endif /* INTERNAL_ONLY */ diff --git a/tests/validation/system_tests/CL/LeNet5.cpp b/tests/validation/system_tests/CL/LeNet5.cpp new file mode 100644 index 0000000000..8b83cfa3a6 --- /dev/null +++ b/tests/validation/system_tests/CL/LeNet5.cpp @@ -0,0 +1,94 @@ +#ifdef INTERNAL_ONLY //FIXME Delete this file before the release +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "CL/CLAccessor.h" +#include "CL/Helper.h" +#include "validation/Validation.h" + +#include "arm_compute/runtime/CL/functions/CLActivationLayer.h" +#include "arm_compute/runtime/CL/functions/CLConvolutionLayer.h" +#include "arm_compute/runtime/CL/functions/CLFullyConnectedLayer.h" +#include "arm_compute/runtime/CL/functions/CLPoolingLayer.h" +#include "arm_compute/runtime/CL/functions/CLSoftmaxLayer.h" + +#include "model_objects/LeNet5.h" + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::cl; +using namespace arm_compute::test::validation; + +namespace +{ +using CLLeNet5Model = model_objects::LeNet5<CLTensor, + CLAccessor, + CLActivationLayer, + CLConvolutionLayer, + CLFullyConnectedLayer, + CLPoolingLayer, + CLSoftmaxLayer>; +std::vector<unsigned int> compute_lenet5(unsigned int batches, std::string input_file) +{ + std::vector<std::string> weight_files = { "cnn_data/lenet_model/conv1_w.dat", + "cnn_data/lenet_model/conv2_w.dat", + "cnn_data/lenet_model/ip1_w.dat", + "cnn_data/lenet_model/ip2_w.dat" + }; + + std::vector<std::string> bias_files = { "cnn_data/lenet_model/conv1_b.dat", + "cnn_data/lenet_model/conv2_b.dat", + "cnn_data/lenet_model/ip1_b.dat", + "cnn_data/lenet_model/ip2_b.dat" + }; + CLLeNet5Model network{}; + network.build(batches); + network.fill(weight_files, bias_files); + network.feed(std::move(input_file)); + network.run(); + + return network.get_classifications(); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(SYSTEM_TESTS) +BOOST_AUTO_TEST_SUITE(CL) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_AUTO_TEST_CASE(LeNet5) +{ + // Compute alexnet + std::vector<unsigned int> classified_labels = compute_lenet5(10, "cnn_data/mnist_data/input100.dat"); + + // Expected labels + std::vector<unsigned int> expected_labels = { 7, 2, 1, 0, 4, 1, 4, 9, 5, 9 }; + + // Validate labels + validate(classified_labels, expected_labels); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif +#endif /* INTERNAL_ONLY */ diff --git a/tests/validation/system_tests/NEON/AlexNet.cpp b/tests/validation/system_tests/NEON/AlexNet.cpp new file mode 100644 index 0000000000..e56110d8de --- /dev/null +++ b/tests/validation/system_tests/NEON/AlexNet.cpp @@ -0,0 +1,112 @@ +#ifdef INTERNAL_ONLY //FIXME Delete this file before the release +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "validation/Validation.h" + +#include "arm_compute/runtime/NEON/functions/NEActivationLayer.h" +#include "arm_compute/runtime/NEON/functions/NEConvolutionLayer.h" +#include "arm_compute/runtime/NEON/functions/NEFullyConnectedLayer.h" +#include "arm_compute/runtime/NEON/functions/NENormalizationLayer.h" +#include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h" +#include "arm_compute/runtime/NEON/functions/NESoftmaxLayer.h" +#include "arm_compute/runtime/SubTensor.h" + +#include "model_objects/AlexNet.h" + +#include <array> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +using NEAlexNetModel = model_objects::AlexNet<ITensor, + Tensor, + SubTensor, + NEAccessor, + NEActivationLayer, + NEConvolutionLayer, + NEFullyConnectedLayer, + NENormalizationLayer, + NEPoolingLayer, + NESoftmaxLayer>; +std::vector<unsigned int> compute_alexnet(unsigned int batches, std::string input_file) +{ + std::vector<std::string> weight_files = { "cnn_data/alexnet_model/conv1_w.dat", + "cnn_data/alexnet_model/conv2_w.dat", + "cnn_data/alexnet_model/conv3_w.dat", + "cnn_data/alexnet_model/conv4_w.dat", + "cnn_data/alexnet_model/conv5_w.dat", + "cnn_data/alexnet_model/fc6_w.dat", + "cnn_data/alexnet_model/fc7_w.dat", + "cnn_data/alexnet_model/fc8_w.dat" + }; + + std::vector<std::string> bias_files = { "cnn_data/alexnet_model/conv1_b.dat", + "cnn_data/alexnet_model/conv2_b.dat", + "cnn_data/alexnet_model/conv3_b.dat", + "cnn_data/alexnet_model/conv4_b.dat", + "cnn_data/alexnet_model/conv5_b.dat", + "cnn_data/alexnet_model/fc6_b.dat", + "cnn_data/alexnet_model/fc7_b.dat", + "cnn_data/alexnet_model/fc8_b.dat" + }; + NEAlexNetModel network{}; + + network.init_weights(batches); + network.build(); + network.allocate(); + network.fill(weight_files, bias_files); + network.feed(std::move(input_file)); + network.run(); + + return network.get_classifications(); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(SYSTEM_TESTS) +BOOST_AUTO_TEST_SUITE(NEON) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_AUTO_TEST_CASE(AlexNet) +{ + // Compute alexnet + std::vector<unsigned int> classified_labels = compute_alexnet(1, "cnn_data/imagenet_data/shark.dat"); + + // Expected labels + std::vector<unsigned int> expected_labels = { 2 }; + + // Validate labels + validate(classified_labels, expected_labels); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif +#endif /* INTERNAL_ONLY */ diff --git a/tests/validation/system_tests/NEON/LeNet5.cpp b/tests/validation/system_tests/NEON/LeNet5.cpp new file mode 100644 index 0000000000..a82b84a997 --- /dev/null +++ b/tests/validation/system_tests/NEON/LeNet5.cpp @@ -0,0 +1,94 @@ +#ifdef INTERNAL_ONLY //FIXME Delete this file before the release +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * 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 "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "validation/Validation.h" + +#include "arm_compute/runtime/NEON/functions/NEActivationLayer.h" +#include "arm_compute/runtime/NEON/functions/NEConvolutionLayer.h" +#include "arm_compute/runtime/NEON/functions/NEFullyConnectedLayer.h" +#include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h" +#include "arm_compute/runtime/NEON/functions/NESoftmaxLayer.h" + +#include "model_objects/LeNet5.h" + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +using NELeNet5Model = model_objects::LeNet5<Tensor, + NEAccessor, + NEActivationLayer, + NEConvolutionLayer, + NEFullyConnectedLayer, + NEPoolingLayer, + NESoftmaxLayer>; +std::vector<unsigned int> compute_lenet5(unsigned int batches, std::string input_file) +{ + std::vector<std::string> weight_files = { "cnn_data/lenet_model/conv1_w.dat", + "cnn_data/lenet_model/conv2_w.dat", + "cnn_data/lenet_model/ip1_w.dat", + "cnn_data/lenet_model/ip2_w.dat" + }; + + std::vector<std::string> bias_files = { "cnn_data/lenet_model/conv1_b.dat", + "cnn_data/lenet_model/conv2_b.dat", + "cnn_data/lenet_model/ip1_b.dat", + "cnn_data/lenet_model/ip2_b.dat" + }; + NELeNet5Model network{}; + network.build(batches); + network.fill(weight_files, bias_files); + network.feed(std::move(input_file)); + network.run(); + + return network.get_classifications(); +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(SYSTEM_TESTS) +BOOST_AUTO_TEST_SUITE(NEON) + +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_AUTO_TEST_CASE(LeNet5) +{ + // Compute alexnet + std::vector<unsigned int> classified_labels = compute_lenet5(10, "cnn_data/mnist_data/input100.dat"); + + // Expected labels + std::vector<unsigned int> expected_labels = { 7, 2, 1, 0, 4, 1, 4, 9, 5, 9 }; + + // Validate labels + validate(classified_labels, expected_labels); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif +#endif /* INTERNAL_ONLY */ |