From c72dabc9ad572493c12c1d9052ec201732b9201e Mon Sep 17 00:00:00 2001 From: Sang-Hoon Park Date: Wed, 27 May 2020 13:03:18 +0100 Subject: COMPMID-3363: Test improvement of Scale on NEON - Create shape dataset for scale kernel, focusing on smaller shapes to be more sensitive to aligned corners - Remove redundant configuration tests - Remove nightly tests - Create new suite for validate() testing - Code cleanup by const variables - Fix fixture issue at computation of minimum scaled size for aligned corners - Extract logic determining scale values for testing. Change-Id: Ifeef354a4d74ed9e6a4e514eded4298e595becbf Signed-off-by: Sang-Hoon Park Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/3273 Tested-by: Arm Jenkins Reviewed-by: Georgios Pinitas Comments-Addressed: Arm Jenkins --- tests/validation/NEON/Scale.cpp | 484 ++++++++++++++++++------------- tests/validation/fixtures/ScaleFixture.h | 87 +++--- 2 files changed, 322 insertions(+), 249 deletions(-) diff --git a/tests/validation/NEON/Scale.cpp b/tests/validation/NEON/Scale.cpp index b7d7b00cb6..82ab666d4a 100644 --- a/tests/validation/NEON/Scale.cpp +++ b/tests/validation/NEON/Scale.cpp @@ -47,6 +47,105 @@ namespace validation { namespace { +using test::datasets::ShapeDataset; + +/** Class to generate boundary values for the given template parameters + * including shapes with large differences between width and height + */ +template +class ScaleShapesBaseDataSet : public ShapeDataset +{ + static constexpr auto boundary_minus_one = element_per_vector * vector_size - 1; + static constexpr auto boundary_plus_one = element_per_vector * vector_size + 1; + static constexpr auto small_size = 3; + +public: + // These tensor shapes are NCHW layout, fixture will convert to NHWC. + ScaleShapesBaseDataSet() + : ShapeDataset("Shape", + { + TensorShape{ small_size, boundary_minus_one, channel, batch }, + TensorShape{ small_size, boundary_plus_one, channel, batch }, + TensorShape{ boundary_minus_one, small_size, channel, batch }, + TensorShape{ boundary_plus_one, small_size, channel, batch }, + TensorShape{ boundary_minus_one, boundary_plus_one, channel, batch }, + TensorShape{ boundary_plus_one, boundary_minus_one, channel, batch }, + }) + { + } +}; + +/** For the single vector, only larger value (+1) than boundary + * since smaller value (-1) could cause some invalid shapes like + * - invalid zero size + * - size 1 which isn't compatible with scale with aligned corners. + */ +template +class ScaleShapesBaseDataSet : public ShapeDataset +{ + static constexpr auto small_size = 3; + static constexpr auto boundary_plus_one = element_per_vector + 1; + +public: + // These tensor shapes are NCHW layout, fixture will convert to NHWC. + ScaleShapesBaseDataSet() + : ShapeDataset("Shape", + { + TensorShape{ small_size, boundary_plus_one, channel, batch }, + TensorShape{ boundary_plus_one, small_size, channel, batch }, + }) + { + } +}; + +/** For the shapes smaller than one vector, only pre-defined tiny shapes + * are tested (3x2, 2x3) as smaller shapes are more likely to cause + * issues and easier to debug. + */ +template +class ScaleShapesBaseDataSet : public ShapeDataset +{ + static constexpr auto small_size = 3; + static constexpr auto zero_vector_boundary_value = 2; + +public: + // These tensor shapes are NCHW layout, fixture will convert to NHWC. + ScaleShapesBaseDataSet() + : ShapeDataset("Shape", + { + TensorShape{ small_size, zero_vector_boundary_value, channel, batch }, + TensorShape{ zero_vector_boundary_value, small_size, channel, batch }, + }) + { + } +}; + +/** Generated shaeps + * - 2D shapes with 0, 1, 2 vector iterations + * - 3D shapes with 0, 1 vector iterations + * - 4D shapes with 0 vector iterations + */ +#define SCALE_SHAPE_DATASET(element_per_vector) \ + concat(concat(concat(concat(concat(ScaleShapesBaseDataSet<1, 1, (element_per_vector), 0>(), \ + ScaleShapesBaseDataSet<1, 1, (element_per_vector), 1>()), \ + ScaleShapesBaseDataSet<1, 1, (element_per_vector), 2>()), \ + ScaleShapesBaseDataSet<3, 3, (element_per_vector), 0>()), \ + ScaleShapesBaseDataSet<3, 3, (element_per_vector), 1>()), \ + ScaleShapesBaseDataSet<3, 7, (element_per_vector), 0>()) + +/** We consider vector size in byte 64 since the maximum size of + * a vector used by @ref ScaleKernelInfo is currently 64-byte (float32x4x4). + * There are possibility to reduce test time further by using + * smaller vector sizes for different data types where applicable. + */ +constexpr uint32_t vector_byte = 64; + +template +constexpr uint32_t num_elements_per_vector() +{ + return vector_byte / sizeof(T); +} + /** Scale data types */ const auto ScaleDataTypes = framework::dataset::make("DataType", { @@ -55,6 +154,13 @@ const auto ScaleDataTypes = framework::dataset::make("DataType", DataType::F32, }); +/** Interpolation policy test set */ +const auto InterpolationPolicySet = framework::dataset::make("InterpolationPolicy", +{ + InterpolationPolicy::NEAREST_NEIGHBOR, + InterpolationPolicy::BILINEAR, +}); + /** Scale data types */ const auto ScaleDataLayouts = framework::dataset::make("DataLayout", { @@ -62,6 +168,13 @@ const auto ScaleDataLayouts = framework::dataset::make("DataLayout", DataLayout::NHWC, }); +/** Sampling policy data set */ +const auto SamplingPolicySet = framework::dataset::make("SamplingPolicy", +{ + SamplingPolicy::TOP_LEFT, + SamplingPolicy::CENTER, +}); + /** Align corners */ const auto AlignCorners = framework::dataset::make("AlignCorners", { @@ -69,6 +182,30 @@ const auto AlignCorners = framework::dataset::make("AlignCorners", true, }); +/** Generating dataset for non-quantized data tyeps with the given shapes */ +#define ASSEMBLE_DATASET(shape) \ + combine(combine(combine(combine(combine((shape), ScaleDataLayouts), \ + InterpolationPolicySet), \ + datasets::BorderModes()), \ + SamplingPolicySet), \ + AlignCorners) + +/** Quantization information data set */ +const auto QuantizationInfoSet = framework::dataset::make("QuantizationInfo", +{ + QuantizationInfo(0.5f, -10), +}); + +/** Generating dataset for quantized data tyeps with the given shapes */ +#define ASSEMBLE_QUANTIZED_DATASET(shape) \ + combine(combine(combine(combine(combine(combine(shape, \ + QuantizationInfoSet), \ + ScaleDataLayouts), \ + InterpolationPolicySet), \ + datasets::BorderModes()), \ + SamplingPolicySet), \ + AlignCorners) + /** Tolerance */ constexpr AbsoluteTolerance tolerance_u8(1); constexpr AbsoluteTolerance tolerance_s16(1); @@ -83,123 +220,151 @@ constexpr float tolerance_num_f32 = 0.01f; TEST_SUITE(NEON) TEST_SUITE(Scale) +TEST_SUITE(Validate) + +/** Validate test suite is to test ARM_COMPUTE_RETURN_ON_* macros + * we use to check the validity of given arguments in @ref NEScale + * and subsequent call to @ref NEScaleKernel. + * Since this is using validate() of @ref NEScale, which pre-adjust + * arguments for @ref NEScaleKernel, the following conditions in + * the kernel are not currently tested. + * - The same input and output + * - Data type of offset, dx and dy + */ -// *INDENT-OFF* -// clang-format off -DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip(zip(zip( - framework::dataset::make("InputInfo", { TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), // Mismatching data type - TensorInfo(TensorShape(4U, 27U, 13U), 1, DataType::F32), // Invalid policy - TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::F32), // Insufficient padding - TensorInfo(TensorShape(4U, 27U, 13U), 1, DataType::F32), - }), - framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(132U, 25U, 2U), 1, DataType::F32), - TensorInfo(TensorShape(4U, 132U, 25U), 1, DataType::F32), - TensorInfo(TensorShape(132U, 25U, 2U), 1, DataType::F32), - TensorInfo(TensorShape(4U, 132U, 25U), 1, DataType::F32), - })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, - InterpolationPolicy::AREA, - InterpolationPolicy::AREA, - InterpolationPolicy::NEAREST_NEIGHBOR, - })), - framework::dataset::make("BorderMode", { BorderMode::UNDEFINED, - BorderMode::UNDEFINED, - BorderMode::UNDEFINED, - BorderMode::REPLICATE, - })), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::CENTER, - SamplingPolicy::CENTER, - SamplingPolicy::CENTER, - SamplingPolicy::CENTER, - })), - framework::dataset::make("DataLayout", { DataLayout::NCHW, - DataLayout::NHWC, - DataLayout::NCHW, - DataLayout::NHWC, - })), - framework::dataset::make("Expected", { false, false, false ,true })), - input_info, output_info, policy,border_mode, sampling_policy, data_layout, expected) +const auto input_shape = TensorShape{ 2, 3, 3, 2 }; +const auto output_shape = TensorShape{ 4, 6, 3, 2 }; + +constexpr auto default_data_type = DataType::U8; +constexpr auto default_data_layout = DataLayout::NHWC; +constexpr auto default_interpolation_policy = InterpolationPolicy::NEAREST_NEIGHBOR; +constexpr auto default_border_mode = BorderMode::UNDEFINED; +constexpr auto default_sampling_policy = SamplingPolicy::CENTER; +constexpr bool default_use_padding = false; + +TEST_CASE(NullPtr, framework::DatasetMode::ALL) { - const PixelValue constant_border(5); - Status status = NEScale::validate(&input_info.clone()->set_is_resizable(false).set_data_layout(data_layout), - &output_info.clone()->set_is_resizable(false).set_data_layout(data_layout), - policy, border_mode, constant_border, sampling_policy); - ARM_COMPUTE_EXPECT(bool(status) == expected, framework::LogLevel::ERRORS); + const auto input = TensorInfo{ input_shape, 1, default_data_type, default_data_layout }; + const auto output = TensorInfo{ output_shape, 1, default_data_type, default_data_layout }; + Status result{}; + + // nullptr is given as input + result = NEScale::validate(nullptr, &output, default_interpolation_policy, default_border_mode); + ARM_COMPUTE_EXPECT(bool(result) == false, framework::LogLevel::ERRORS); + + // nullptr is given as output + result = NEScale::validate(&input, nullptr, default_interpolation_policy, default_border_mode); + ARM_COMPUTE_EXPECT(bool(result) == false, framework::LogLevel::ERRORS); } -// clang-format on -// *INDENT-ON* - -DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(datasets::SmallShapes(), ScaleDataTypes), ScaleDataLayouts), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::CENTER })), - shape, data_type, data_layout, policy, border_mode, sampling_policy) + +TEST_CASE(SupportDataType, framework::DatasetMode::ALL) { - std::mt19937 generator(library->seed()); - std::uniform_real_distribution distribution_float(0.25, 2); - const float scale_x = distribution_float(generator); - const float scale_y = distribution_float(generator); - uint8_t constant_border_value = 0; - TensorShape src_shape = shape; - if(border_mode == BorderMode::CONSTANT) + const std::map supported_data_types = { - std::uniform_int_distribution distribution_u8(0, 255); - constant_border_value = distribution_u8(generator); - } - - // Get width/height indices depending on layout - const int idx_width = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH); - const int idx_height = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT); - - // Change shape in case of NHWC. - if(data_layout == DataLayout::NHWC) + { DataType::U8, true }, + { DataType::S8, false }, + { DataType::QSYMM8, false }, + { DataType::QASYMM8, true }, + { DataType::QASYMM8_SIGNED, true }, + { DataType::QSYMM8_PER_CHANNEL, false }, + { DataType::U16, false }, + { DataType::S16, true }, + { DataType::QSYMM16, false }, + { DataType::QASYMM16, false }, + { DataType::U32, false }, + { DataType::S32, false }, + { DataType::U64, false }, + { DataType::S64, false }, + { DataType::BFLOAT16, false }, +#ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC + { DataType::F16, true }, +#else // __ARM_FEATURE_FP16_VECTOR_ARITHMETIC + { DataType::F16, false }, +#endif // __ARM_FEATURE_FP16_VECTOR_ARITHMETIC + { DataType::F32, true }, + { DataType::F64, false }, + { DataType::SIZET, false }, + }; + Status result{}; + for(auto &kv : supported_data_types) { - permute(src_shape, PermutationVector(2U, 0U, 1U)); + const auto input = TensorInfo{ input_shape, 1, kv.first, default_data_layout }; + const auto output = TensorInfo{ output_shape, 1, kv.first, default_data_layout }; + result = NEScale::validate(&input, &output, default_interpolation_policy, default_border_mode); + ARM_COMPUTE_EXPECT(bool(result) == kv.second, framework::LogLevel::ERRORS); } +} - // Calculate scaled shape - TensorShape shape_scaled(src_shape); - shape_scaled.set(idx_width, src_shape[idx_width] * scale_x); - shape_scaled.set(idx_height, src_shape[idx_height] * scale_y); +TEST_CASE(MissmatchingDataType, framework::DatasetMode::ALL) +{ + constexpr auto non_default_data_type = DataType::F32; - // Create tensors - Tensor src = create_tensor(src_shape, data_type, 1, QuantizationInfo(), data_layout); - Tensor dst = create_tensor(shape_scaled, data_type, 1, QuantizationInfo(), data_layout); + const auto input = TensorInfo{ input_shape, 1, default_data_type, default_data_layout }; + const auto output = TensorInfo{ output_shape, 1, non_default_data_type, default_data_layout }; + Status result{}; + result = NEScale::validate(&input, &output, default_interpolation_policy, default_border_mode); + ARM_COMPUTE_EXPECT(bool(result) == false, framework::LogLevel::ERRORS); +} - ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); - ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); +TEST_CASE(UsePadding, framework::DatasetMode::ALL) +{ + const auto input = TensorInfo{ input_shape, 1, default_data_type, default_data_layout }; + const auto output = TensorInfo{ output_shape, 1, default_data_type, default_data_layout }; + Status result{}; - // Create and configure function - NEScale nescale; - nescale.configure(&src, &dst, policy, border_mode, constant_border_value, sampling_policy); + // When use padding is false, border mode should be constant + constexpr auto border_mode = BorderMode::UNDEFINED; + constexpr bool use_padding = false; - // Validate valid region - const ValidRegion dst_valid_region = calculate_valid_region_scale(*(src.info()), shape_scaled, policy, sampling_policy, (border_mode == BorderMode::UNDEFINED)); - validate(dst.info()->valid_region(), dst_valid_region); + result = NEScale::validate(&input, &output, default_interpolation_policy, border_mode, PixelValue(), default_sampling_policy, use_padding); + ARM_COMPUTE_EXPECT(bool(result) == false, framework::LogLevel::ERRORS); +} - // Validate padding - int num_elements_processed_x = 16; - if(data_layout == DataLayout::NHWC) - { - num_elements_processed_x = (policy == InterpolationPolicy::BILINEAR) ? 1 : 16 / src.info()->element_size(); - } - PaddingCalculator calculator(shape_scaled.x(), num_elements_processed_x); - calculator.set_border_mode(border_mode); +TEST_CASE(AreaWithNHWC, framework::DatasetMode::ALL) +{ + // InterpolationPolicy::AREA is not supported for NHWC + constexpr auto interpolation_policy = InterpolationPolicy::AREA; + constexpr auto data_layout = DataLayout::NHWC; + + const auto input = TensorInfo{ input_shape, 1, default_data_type, data_layout }; + const auto output = TensorInfo{ output_shape, 1, default_data_type, data_layout }; + Status result{}; + result = NEScale::validate(&input, &output, interpolation_policy, default_border_mode); + ARM_COMPUTE_EXPECT(bool(result) == false, framework::LogLevel::ERRORS); +} - PaddingSize read_padding(1); - if(data_layout == DataLayout::NHWC) - { - read_padding = calculator.required_padding(PaddingCalculator::Option::EXCLUDE_BORDER); - if(border_mode != BorderMode::REPLICATE && policy == InterpolationPolicy::BILINEAR) - { - read_padding.top = 1; - } - } - const PaddingSize write_padding = calculator.required_padding(PaddingCalculator::Option::EXCLUDE_BORDER); - validate(src.info()->padding(), read_padding); - validate(dst.info()->padding(), write_padding); +TEST_CASE(AreaWithNonU8, framework::DatasetMode::ALL) +{ + // InterpolationPolicy::AREA only supports U8 + constexpr auto interpolation_policy = InterpolationPolicy::AREA; + constexpr auto data_type = DataType::F32; + constexpr auto data_layout = DataLayout::NCHW; + + const auto input = TensorInfo{ input_shape, 1, data_type, data_layout }; + const auto output = TensorInfo{ output_shape, 1, data_type, data_layout }; + Status result{}; + result = NEScale::validate(&input, &output, interpolation_policy, default_border_mode); + ARM_COMPUTE_EXPECT(bool(result) == false, framework::LogLevel::ERRORS); } +TEST_CASE(InvalidAlignedCornerOutput, framework::DatasetMode::ALL) +{ + // Bilinear with aligned corners require at least 2x2 output to prevent overflow. + // Also, aligned corners require sampling policy to be TOP_LEFT. + constexpr auto interpolation_policy = InterpolationPolicy::BILINEAR; + constexpr bool align_corners = true; + constexpr auto sampling_policy = SamplingPolicy::TOP_LEFT; + const auto invalid_output_shape = TensorShape{ 1, 1, 3, 2 }; + + const auto input = TensorInfo{ input_shape, 1, default_data_type, default_data_layout }; + const auto output = TensorInfo{ invalid_output_shape, 1, default_data_type, default_data_layout }; + Status result{}; + result = NEScale::validate(&input, &output, interpolation_policy, default_border_mode, PixelValue(), sampling_policy, default_use_padding, align_corners); + ARM_COMPUTE_EXPECT(bool(result) == false, framework::LogLevel::ERRORS); +} + +TEST_SUITE_END() // Validate + template using NEScaleFixture = ScaleValidationFixture; template @@ -207,28 +372,8 @@ using NEScaleQuantizedFixture = ScaleValidationQuantizedFixture, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(datasets::SmallShapes(), framework::dataset::make("DataType", - DataType::F32)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) -{ - //Create valid region - TensorInfo src_info(_shape, 1, _data_type); - ValidRegion valid_region = calculate_valid_region_scale(src_info, _reference.shape(), _policy, _sampling_policy, (_border_mode == BorderMode::UNDEFINED)); - - // Validate output - validate(Accessor(_target), _reference, valid_region, tolerance_f32, tolerance_num_f32); -} -FIXTURE_DATA_TEST_CASE(RunLarge, NEScaleFixture, framework::DatasetMode::NIGHTLY, combine(combine(combine(combine(combine(combine(datasets::LargeShapes(), framework::dataset::make("DataType", - DataType::F32)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) +const auto f32_shape = combine((SCALE_SHAPE_DATASET(num_elements_per_vector())), framework::dataset::make("DataType", DataType::F32)); +FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleFixture, framework::DatasetMode::ALL, ASSEMBLE_DATASET(f32_shape)) { //Create valid region TensorInfo src_info(_shape, 1, _data_type); @@ -240,28 +385,8 @@ FIXTURE_DATA_TEST_CASE(RunLarge, NEScaleFixture, framework::DatasetMode:: TEST_SUITE_END() // FP32 #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC TEST_SUITE(FP16) -FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleFixture, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(datasets::SmallShapes(), framework::dataset::make("DataType", - DataType::F16)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) -{ - //Create valid region - TensorInfo src_info(_shape, 1, _data_type); - const ValidRegion valid_region = calculate_valid_region_scale(src_info, _reference.shape(), _policy, _sampling_policy, (_border_mode == BorderMode::UNDEFINED)); - - // Validate output - validate(Accessor(_target), _reference, valid_region, tolerance_f16); -} -FIXTURE_DATA_TEST_CASE(RunLarge, NEScaleFixture, framework::DatasetMode::NIGHTLY, combine(combine(combine(combine(combine(combine(datasets::LargeShapes(), framework::dataset::make("DataType", - DataType::F16)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) +const auto f16_shape = combine((SCALE_SHAPE_DATASET(num_elements_per_vector())), framework::dataset::make("DataType", DataType::F16)); +FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleFixture, framework::DatasetMode::ALL, ASSEMBLE_DATASET(f16_shape)) { //Create valid region TensorInfo src_info(_shape, 1, _data_type); @@ -276,28 +401,8 @@ TEST_SUITE_END() // Float TEST_SUITE(Integer) TEST_SUITE(U8) -FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleFixture, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(datasets::SmallShapes(), framework::dataset::make("DataType", - DataType::U8)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) -{ - //Create valid region - TensorInfo src_info(_shape, 1, _data_type); - ValidRegion valid_region = calculate_valid_region_scale(src_info, _reference.shape(), _policy, _sampling_policy, (_border_mode == BorderMode::UNDEFINED)); - - // Validate output - validate(Accessor(_target), _reference, valid_region, tolerance_u8); -} -FIXTURE_DATA_TEST_CASE(RunLarge, NEScaleFixture, framework::DatasetMode::NIGHTLY, combine(combine(combine(combine(combine(combine(datasets::LargeShapes(), framework::dataset::make("DataType", - DataType::U8)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) +const auto u8_shape = combine((SCALE_SHAPE_DATASET(num_elements_per_vector())), framework::dataset::make("DataType", DataType::U8)); +FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleFixture, framework::DatasetMode::ALL, ASSEMBLE_DATASET(u8_shape)) { //Create valid region TensorInfo src_info(_shape, 1, _data_type); @@ -308,28 +413,8 @@ FIXTURE_DATA_TEST_CASE(RunLarge, NEScaleFixture, framework::DatasetMode } TEST_SUITE_END() // U8 TEST_SUITE(S16) -FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleFixture, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(datasets::SmallShapes(), framework::dataset::make("DataType", - DataType::S16)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) -{ - //Create valid region - TensorInfo src_info(_shape, 1, _data_type); - ValidRegion valid_region = calculate_valid_region_scale(src_info, _reference.shape(), _policy, _sampling_policy, (_border_mode == BorderMode::UNDEFINED)); - - // Validate output - validate(Accessor(_target), _reference, valid_region, tolerance_s16, tolerance_num_s16); -} -FIXTURE_DATA_TEST_CASE(RunLarge, NEScaleFixture, framework::DatasetMode::NIGHTLY, combine(combine(combine(combine(combine(combine(datasets::LargeShapes(), framework::dataset::make("DataType", - DataType::S16)), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) +const auto s16_shape = combine((SCALE_SHAPE_DATASET(num_elements_per_vector())), framework::dataset::make("DataType", DataType::S16)); +FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleFixture, framework::DatasetMode::ALL, ASSEMBLE_DATASET(s16_shape)) { //Create valid region TensorInfo src_info(_shape, 1, _data_type); @@ -343,14 +428,8 @@ TEST_SUITE_END() // Integer TEST_SUITE(Quantized) TEST_SUITE(QASYMM8) -FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleQuantizedFixture, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(combine(datasets::SmallShapes(), - framework::dataset::make("DataType", DataType::QASYMM8)), - framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.5f, -10) })), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) +const auto qasymm8_shape = combine((SCALE_SHAPE_DATASET(num_elements_per_vector())), framework::dataset::make("DataType", DataType::QASYMM8)); +FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleQuantizedFixture, framework::DatasetMode::ALL, ASSEMBLE_QUANTIZED_DATASET(qasymm8_shape)) { //Create valid region TensorInfo src_info(_shape, 1, _data_type); @@ -360,16 +439,9 @@ FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleQuantizedFixture, framework::Da validate(Accessor(_target), _reference, valid_region, tolerance_u8); } TEST_SUITE_END() // QASYMM8 - TEST_SUITE(QASYMM8_SIGNED) -FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleQuantizedFixture, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(combine(datasets::SmallShapes(), - framework::dataset::make("DataType", DataType::QASYMM8_SIGNED)), - framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.5f, -10) })), - framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), - framework::dataset::make("InterpolationPolicy", { InterpolationPolicy::NEAREST_NEIGHBOR, InterpolationPolicy::BILINEAR })), - datasets::BorderModes()), - framework::dataset::make("SamplingPolicy", { SamplingPolicy::TOP_LEFT, SamplingPolicy::CENTER })), - AlignCorners)) +const auto qasymm8_signed_shape = combine((SCALE_SHAPE_DATASET(num_elements_per_vector())), framework::dataset::make("DataType", DataType::QASYMM8_SIGNED)); +FIXTURE_DATA_TEST_CASE(RunSmall, NEScaleQuantizedFixture, framework::DatasetMode::ALL, ASSEMBLE_QUANTIZED_DATASET(qasymm8_signed_shape)) { //Create valid region TensorInfo src_info(_shape, 1, _data_type); diff --git a/tests/validation/fixtures/ScaleFixture.h b/tests/validation/fixtures/ScaleFixture.h index e3846eda67..cf3c5c818f 100644 --- a/tests/validation/fixtures/ScaleFixture.h +++ b/tests/validation/fixtures/ScaleFixture.h @@ -48,52 +48,54 @@ public: void setup(TensorShape shape, DataType data_type, QuantizationInfo quantization_info, DataLayout data_layout, InterpolationPolicy policy, BorderMode border_mode, SamplingPolicy sampling_policy, bool align_corners) { - constexpr float max_width = 8192.0f; - constexpr float max_height = 6384.0f; - _shape = shape; _policy = policy; _border_mode = border_mode; _sampling_policy = sampling_policy; _data_type = data_type; _quantization_info = quantization_info; - _align_corners = align_corners; + _align_corners = align_corners && _policy == InterpolationPolicy::BILINEAR && _sampling_policy == SamplingPolicy::TOP_LEFT; - std::mt19937 generator(library->seed()); - std::uniform_real_distribution distribution_float(0.25, 3); - float scale_x = distribution_float(generator); - float scale_y = distribution_float(generator); + generate_scale(shape); - const int idx_width = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH); - const int idx_height = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT); + std::mt19937 generator(library->seed()); + std::uniform_int_distribution distribution_u8(0, 255); + _constant_border_value = static_cast(distribution_u8(generator)); - scale_x = ((shape[idx_width] * scale_x) > max_width) ? (max_width / shape[idx_width]) : scale_x; - scale_y = ((shape[idx_height] * scale_y) > max_height) ? (max_height / shape[idx_height]) : scale_y; + _target = compute_target(shape, data_layout); + _reference = compute_reference(shape); + } - const bool align_corners_a = policy == InterpolationPolicy::BILINEAR - && sampling_policy == SamplingPolicy::TOP_LEFT - && align_corners; +protected: + void generate_scale(const TensorShape &shape) + { + static constexpr float _min_scale{ 0.25f }; + static constexpr float _max_scale{ 3.f }; - if(align_corners_a) + constexpr float max_width{ 8192.0f }; + constexpr float max_height{ 6384.0f }; + + const float min_width = _align_corners ? 2.f : 1.f; + const float min_height = _align_corners ? 2.f : 1.f; + + std::mt19937 generator(library->seed()); + std::uniform_real_distribution distribution_float(_min_scale, _max_scale); + + auto generate = [&](size_t input_size, float min_output, float max_output) -> float { - /* When align_corners = true is used for bilinear, both width and height - * of output should be > 1 to avoid overflow during computation otherwise - * it fails while checking argument values. - */ - constexpr float min_width = 2.f; - constexpr float min_height = 2.f; - scale_x = ((shape[idx_width] * scale_x) < min_width) ? (min_width / shape[idx_width]) : scale_x; - scale_y = ((shape[idx_height] * scale_y) < min_height) ? (min_height / shape[idx_height]) : scale_y; - } + const float generated_scale = distribution_float(generator); + const float output_size = utility::clamp(static_cast(input_size) * generated_scale, min_output, max_output); + return output_size / input_size; + }; - std::uniform_int_distribution distribution_u8(0, 255); - T constant_border_value = static_cast(distribution_u8(generator)); + // Input shape is always given in NCHW layout. NHWC is dealt by permute in compute_target() + const int idx_width = get_data_layout_dimension_index(DataLayout::NCHW, DataLayoutDimension::WIDTH); + const int idx_height = get_data_layout_dimension_index(DataLayout::NCHW, DataLayoutDimension::HEIGHT); - _target = compute_target(shape, data_layout, scale_x, scale_y, policy, border_mode, constant_border_value, sampling_policy, quantization_info); - _reference = compute_reference(shape, scale_x, scale_y, policy, border_mode, constant_border_value, sampling_policy, quantization_info); + _scale_x = generate(shape[idx_width], min_width, max_width); + _scale_y = generate(shape[idx_height], min_height, max_height); } -protected: template void fill(U &&tensor) { @@ -114,9 +116,7 @@ protected: } } - TensorType compute_target(TensorShape shape, DataLayout data_layout, const float scale_x, const float scale_y, - InterpolationPolicy policy, BorderMode border_mode, T constant_border_value, SamplingPolicy sampling_policy, - QuantizationInfo quantization_info) + TensorType compute_target(TensorShape shape, DataLayout data_layout) { // Change shape in case of NHWC. if(data_layout == DataLayout::NHWC) @@ -125,20 +125,20 @@ protected: } // Create tensors - TensorType src = create_tensor(shape, _data_type, 1, quantization_info, data_layout); + TensorType src = create_tensor(shape, _data_type, 1, _quantization_info, data_layout); const int idx_width = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH); const int idx_height = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT); TensorShape shape_scaled(shape); - shape_scaled.set(idx_width, shape[idx_width] * scale_x); - shape_scaled.set(idx_height, shape[idx_height] * scale_y); - TensorType dst = create_tensor(shape_scaled, _data_type, 1, quantization_info, data_layout); + shape_scaled.set(idx_width, shape[idx_width] * _scale_x); + shape_scaled.set(idx_height, shape[idx_height] * _scale_y); + TensorType dst = create_tensor(shape_scaled, _data_type, 1, _quantization_info, data_layout); // Create and configure function FunctionType scale; - scale.configure(&src, &dst, policy, border_mode, constant_border_value, sampling_policy, /* use_padding */ true, _align_corners); + scale.configure(&src, &dst, _policy, _border_mode, _constant_border_value, _sampling_policy, /* use_padding */ true, _align_corners); ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); @@ -158,17 +158,15 @@ protected: return dst; } - SimpleTensor compute_reference(const TensorShape &shape, const float scale_x, const float scale_y, - InterpolationPolicy policy, BorderMode border_mode, T constant_border_value, SamplingPolicy sampling_policy, - QuantizationInfo quantization_info) + SimpleTensor compute_reference(const TensorShape &shape) { // Create reference - SimpleTensor src{ shape, _data_type, 1, quantization_info }; + SimpleTensor src{ shape, _data_type, 1, _quantization_info }; // Fill reference fill(src); - return reference::scale(src, scale_x, scale_y, policy, border_mode, constant_border_value, sampling_policy, /* ceil_policy_scale */ false, _align_corners); + return reference::scale(src, _scale_x, _scale_y, _policy, _border_mode, _constant_border_value, _sampling_policy, /* ceil_policy_scale */ false, _align_corners); } TensorType _target{}; @@ -176,10 +174,13 @@ protected: TensorShape _shape{}; InterpolationPolicy _policy{}; BorderMode _border_mode{}; + T _constant_border_value{}; SamplingPolicy _sampling_policy{}; DataType _data_type{}; QuantizationInfo _quantization_info{}; bool _align_corners{ false }; + float _scale_x{ 1.f }; + float _scale_y{ 1.f }; }; template -- cgit v1.2.1