John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 1 | /* |
Manuel Bottini | 79fa9a2 | 2019-02-22 17:54:22 +0000 | [diff] [blame] | 2 | * Copyright (c) 2017-2019 ARM Limited. |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 3 | * |
| 4 | * SPDX-License-Identifier: MIT |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to |
| 8 | * deal in the Software without restriction, including without limitation the |
| 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| 10 | * sell copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in all |
| 14 | * copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
Manuel Bottini | 79fa9a2 | 2019-02-22 17:54:22 +0000 | [diff] [blame] | 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 22 | * SOFTWARE. |
| 23 | */ |
| 24 | #include "PixelWiseMultiplication.h" |
| 25 | |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 26 | #include "tests/validation/Helpers.h" |
| 27 | |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 28 | namespace arm_compute |
| 29 | { |
| 30 | namespace test |
| 31 | { |
| 32 | namespace validation |
| 33 | { |
| 34 | namespace reference |
| 35 | { |
| 36 | template <class T> |
| 37 | struct is_floating_point |
| 38 | : std::integral_constant < bool, |
| 39 | std::is_same<float, typename std::remove_cv<T>::type>::value || std::is_same<half_float::half, typename std::remove_cv<T>::type>::value |
| 40 | || std::is_same<double, typename std::remove_cv<T>::type>::value || std::is_same<long double, typename std::remove_cv<T>::type>::value > |
| 41 | { |
| 42 | }; |
| 43 | |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 44 | namespace |
| 45 | { |
| 46 | /** Compute the result of `src1 * src2 * scale`. The result type always matches the type of @p src2. |
| 47 | * |
Vidhya Sudhan Loganathan | 0fc2545 | 2018-06-18 14:40:56 +0100 | [diff] [blame] | 48 | * @param[in] src1 An input value. Data types supported: U8/S16/F16/F32. |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 49 | * @param[in] src2 An input value. Data types supported: same as @p src1. |
| 50 | * @param[in] scale Scale to apply after multiplication. |
Vidhya Sudhan Loganathan | 0fc2545 | 2018-06-18 14:40:56 +0100 | [diff] [blame] | 51 | * Scale must be positive and its value must be either 1/255 or 1/2^n where n is between 0 and 15. |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 52 | * @param[in] convert_policy Overflow policy. Supported overflow policies: Wrap, Saturate |
| 53 | * @param[in] rounding_policy Rounding policy. Supported rounding modes: to zero, to nearest even. |
| 54 | */ |
| 55 | template <typename T1, typename T2> |
| 56 | T2 mul(const T1 src1, const T2 src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) |
| 57 | { |
| 58 | using intermediate_type = typename common_promoted_signed_type<T1, T2, T2>::intermediate_type; |
| 59 | |
| 60 | const double val = static_cast<intermediate_type>(src1) * static_cast<intermediate_type>(src2) * static_cast<double>(scale); |
| 61 | |
| 62 | if(is_floating_point<T2>::value) |
| 63 | { |
| 64 | const auto result = static_cast<T2>(val); |
| 65 | |
| 66 | return result; |
| 67 | } |
| 68 | else |
| 69 | { |
| 70 | double rounded_val = 0; |
| 71 | switch(rounding_policy) |
| 72 | { |
| 73 | case(RoundingPolicy::TO_ZERO): |
| 74 | rounded_val = support::cpp11::trunc(val); |
| 75 | break; |
| 76 | case(RoundingPolicy::TO_NEAREST_UP): |
| 77 | rounded_val = round_half_up(val); |
| 78 | break; |
| 79 | case(RoundingPolicy::TO_NEAREST_EVEN): |
| 80 | rounded_val = round_half_even(val); |
| 81 | break; |
| 82 | default: |
| 83 | ARM_COMPUTE_ERROR("Unsupported rounding policy"); |
| 84 | } |
| 85 | |
| 86 | const auto result = static_cast<T2>((convert_policy == ConvertPolicy::SATURATE) ? saturate_cast<T2>(rounded_val) : rounded_val); |
| 87 | |
| 88 | return result; |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | template <size_t dim> |
| 93 | struct BroadcastUnroll |
| 94 | { |
| 95 | template <typename T1, typename T2> |
| 96 | static void unroll(const SimpleTensor<T1> &src1, const SimpleTensor<T2> &src2, SimpleTensor<T2> &dst, |
| 97 | float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
| 98 | Coordinates &id_src1, Coordinates &id_src2, Coordinates &id_dst) |
| 99 | { |
| 100 | const bool src1_is_broadcast = (src1.shape()[dim - 1] != dst.shape()[dim - 1]); |
| 101 | const bool src2_is_broadcast = (src2.shape()[dim - 1] != dst.shape()[dim - 1]); |
| 102 | |
| 103 | id_src1.set(dim - 1, 0); |
| 104 | id_src2.set(dim - 1, 0); |
| 105 | id_dst.set(dim - 1, 0); |
| 106 | |
| 107 | for(size_t i = 0; i < dst.shape()[dim - 1]; ++i, ++id_dst[dim - 1]) |
| 108 | { |
| 109 | BroadcastUnroll < dim - 1 >::unroll(src1, src2, dst, scale, convert_policy, rounding_policy, id_src1, id_src2, id_dst); |
| 110 | |
| 111 | id_src1[dim - 1] += !src1_is_broadcast; |
| 112 | id_src2[dim - 1] += !src2_is_broadcast; |
| 113 | } |
| 114 | } |
| 115 | }; |
| 116 | |
| 117 | template <> |
| 118 | struct BroadcastUnroll<0> |
| 119 | { |
| 120 | template <typename T1, typename T2> |
| 121 | static void unroll(const SimpleTensor<T1> &src1, const SimpleTensor<T2> &src2, SimpleTensor<T2> &dst, |
| 122 | float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
| 123 | Coordinates &id_src1, Coordinates &id_src2, Coordinates &id_dst) |
| 124 | { |
| 125 | dst[coord2index(dst.shape(), id_dst)] = mul(src1[coord2index(src1.shape(), id_src1)], src2[coord2index(src2.shape(), id_src2)], scale, convert_policy, rounding_policy); |
| 126 | } |
| 127 | }; |
| 128 | } // namespace |
| 129 | |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 130 | template <typename T1, typename T2> |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 131 | SimpleTensor<T2> pixel_wise_multiplication(const SimpleTensor<T1> &src1, const SimpleTensor<T2> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, QuantizationInfo qout) |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 132 | { |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 133 | ARM_COMPUTE_UNUSED(qout); |
| 134 | |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 135 | SimpleTensor<T2> dst(TensorShape::broadcast_shape(src1.shape(), src2.shape()), src2.data_type()); |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 136 | |
| 137 | if(scale < 0) |
| 138 | { |
| 139 | ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative"); |
| 140 | } |
| 141 | |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 142 | Coordinates id_src1, id_src2, id_dst; |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 143 | |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 144 | BroadcastUnroll<Coordinates::num_max_dimensions>::unroll(src1, src2, dst, scale, convert_policy, rounding_policy, id_src1, id_src2, id_dst); |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 145 | |
| 146 | return dst; |
| 147 | } |
| 148 | |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 149 | template <> |
| 150 | SimpleTensor<uint8_t> pixel_wise_multiplication(const SimpleTensor<uint8_t> &src1, const SimpleTensor<uint8_t> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
| 151 | QuantizationInfo qout) |
| 152 | { |
| 153 | SimpleTensor<uint8_t> dst(TensorShape::broadcast_shape(src1.shape(), src2.shape()), src2.data_type(), 1, qout); |
| 154 | |
| 155 | if(src1.data_type() == DataType::QASYMM8 && src2.data_type() == DataType::QASYMM8) |
| 156 | { |
| 157 | SimpleTensor<float> src1_tmp = convert_from_asymmetric(src1); |
| 158 | SimpleTensor<float> src2_tmp = convert_from_asymmetric(src2); |
| 159 | SimpleTensor<float> dst_tmp = pixel_wise_multiplication<float>(src1_tmp, src2_tmp, scale, convert_policy, rounding_policy, qout); |
| 160 | dst = convert_to_asymmetric(dst_tmp, qout); |
| 161 | } |
| 162 | else |
| 163 | { |
| 164 | if(scale < 0) |
| 165 | { |
| 166 | ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative"); |
| 167 | } |
| 168 | |
| 169 | Coordinates id_src1, id_src2, id_dst; |
| 170 | BroadcastUnroll<Coordinates::num_max_dimensions>::unroll(src1, src2, dst, scale, convert_policy, rounding_policy, id_src1, id_src2, id_dst); |
| 171 | } |
| 172 | return dst; |
| 173 | } |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 174 | // *INDENT-OFF* |
| 175 | // clang-format off |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 176 | template SimpleTensor<int16_t> pixel_wise_multiplication(const SimpleTensor<uint8_t> &src1, const SimpleTensor<int16_t> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, QuantizationInfo qout); |
| 177 | template SimpleTensor<int16_t> pixel_wise_multiplication(const SimpleTensor<int16_t> &src1, const SimpleTensor<int16_t> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, QuantizationInfo qout); |
| 178 | template SimpleTensor<float> pixel_wise_multiplication(const SimpleTensor<float> &src1, const SimpleTensor<float> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, QuantizationInfo qout); |
| 179 | template SimpleTensor<half_float::half> pixel_wise_multiplication(const SimpleTensor<half_float::half> &src1, const SimpleTensor<half_float::half> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, QuantizationInfo qout); |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 180 | // clang-format on |
| 181 | // *INDENT-ON* |
| 182 | } // namespace reference |
| 183 | } // namespace validation |
| 184 | } // namespace test |
| 185 | } // namespace arm_compute |