John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 1 | /* |
Michele Di Giorgio | d9eaf61 | 2020-07-08 11:12:57 +0100 | [diff] [blame] | 2 | * Copyright (c) 2017-2020 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 | */ |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 55 | template <typename T1, typename T2, typename T3> |
| 56 | T3 mul(const T1 src1, const T2 src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy) |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 57 | { |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 58 | using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type; |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 59 | |
| 60 | const double val = static_cast<intermediate_type>(src1) * static_cast<intermediate_type>(src2) * static_cast<double>(scale); |
| 61 | |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 62 | if(is_floating_point<T3>::value) |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 63 | { |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 64 | const auto result = static_cast<T3>(val); |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 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 | |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 86 | const auto result = static_cast<T3>((convert_policy == ConvertPolicy::SATURATE) ? saturate_cast<T3>(rounded_val) : rounded_val); |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 87 | |
| 88 | return result; |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | template <size_t dim> |
| 93 | struct BroadcastUnroll |
| 94 | { |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 95 | template <typename T1, typename T2, typename T3> |
| 96 | static void unroll(const SimpleTensor<T1> &src1, const SimpleTensor<T2> &src2, SimpleTensor<T3> &dst, |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 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 | { |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 120 | template <typename T1, typename T2, typename T3> |
| 121 | static void unroll(const SimpleTensor<T1> &src1, const SimpleTensor<T2> &src2, SimpleTensor<T3> &dst, |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 122 | float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
| 123 | Coordinates &id_src1, Coordinates &id_src2, Coordinates &id_dst) |
| 124 | { |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 125 | dst[coord2index(dst.shape(), id_dst)] = mul<T1, T2, T3>(src1[coord2index(src1.shape(), id_src1)], src2[coord2index(src2.shape(), id_src2)], scale, convert_policy, rounding_policy); |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 126 | } |
| 127 | }; |
| 128 | } // namespace |
| 129 | |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 130 | template <typename T1, typename T2, typename T3> |
| 131 | SimpleTensor<T3> pixel_wise_multiplication(const SimpleTensor<T1> &src1, const SimpleTensor<T2> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
| 132 | DataType dt_out, const QuantizationInfo &qout) |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 133 | { |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 134 | ARM_COMPUTE_UNUSED(qout); |
| 135 | |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 136 | SimpleTensor<T3> dst(TensorShape::broadcast_shape(src1.shape(), src2.shape()), dt_out); |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 137 | |
| 138 | if(scale < 0) |
| 139 | { |
| 140 | ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative"); |
| 141 | } |
| 142 | |
Michalis Spyrou | bcfd09a | 2019-05-01 13:03:59 +0100 | [diff] [blame] | 143 | Coordinates id_src1{}; |
| 144 | Coordinates id_src2{}; |
| 145 | Coordinates id_dst{}; |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 146 | |
Michele Di Giorgio | 6259e5f | 2018-01-17 17:29:33 +0000 | [diff] [blame] | 147 | 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] | 148 | |
| 149 | return dst; |
| 150 | } |
| 151 | |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 152 | template <> |
| 153 | SimpleTensor<uint8_t> pixel_wise_multiplication(const SimpleTensor<uint8_t> &src1, const SimpleTensor<uint8_t> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 154 | DataType dt_out, const QuantizationInfo &qout) |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 155 | { |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 156 | SimpleTensor<uint8_t> dst(TensorShape::broadcast_shape(src1.shape(), src2.shape()), dt_out, 1, qout); |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 157 | |
| 158 | if(src1.data_type() == DataType::QASYMM8 && src2.data_type() == DataType::QASYMM8) |
| 159 | { |
| 160 | SimpleTensor<float> src1_tmp = convert_from_asymmetric(src1); |
| 161 | SimpleTensor<float> src2_tmp = convert_from_asymmetric(src2); |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 162 | SimpleTensor<float> dst_tmp = pixel_wise_multiplication<float, float, float>(src1_tmp, src2_tmp, scale, convert_policy, rounding_policy, DataType::F32, qout); |
Michele Di Giorgio | 4aff98f | 2019-08-28 16:27:26 +0100 | [diff] [blame] | 163 | dst = convert_to_asymmetric<uint8_t>(dst_tmp, qout); |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 164 | } |
| 165 | else |
| 166 | { |
| 167 | if(scale < 0) |
| 168 | { |
| 169 | ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative"); |
| 170 | } |
| 171 | |
Michalis Spyrou | bcfd09a | 2019-05-01 13:03:59 +0100 | [diff] [blame] | 172 | Coordinates id_src1{}; |
| 173 | Coordinates id_src2{}; |
| 174 | Coordinates id_dst{}; |
Georgios Pinitas | bf28a3c | 2018-09-18 14:34:48 +0100 | [diff] [blame] | 175 | BroadcastUnroll<Coordinates::num_max_dimensions>::unroll(src1, src2, dst, scale, convert_policy, rounding_policy, id_src1, id_src2, id_dst); |
| 176 | } |
| 177 | return dst; |
| 178 | } |
Michele Di Giorgio | d8a468f | 2019-06-19 15:34:41 +0100 | [diff] [blame] | 179 | |
| 180 | template <> |
Sheri Zhang | fcf6f4e | 2020-06-25 20:01:00 +0100 | [diff] [blame] | 181 | SimpleTensor<int16_t> pixel_wise_multiplication(const SimpleTensor<uint8_t> &src1, const SimpleTensor<uint8_t> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
| 182 | DataType dt_out, const QuantizationInfo &qout) |
| 183 | { |
| 184 | SimpleTensor<int16_t> dst(TensorShape::broadcast_shape(src1.shape(), src2.shape()), dt_out, 1, qout); |
| 185 | |
| 186 | if(src1.data_type() == DataType::QASYMM8 && src2.data_type() == DataType::QASYMM8) |
| 187 | { |
| 188 | SimpleTensor<float> src1_tmp = convert_from_asymmetric(src1); |
| 189 | SimpleTensor<float> src2_tmp = convert_from_asymmetric(src2); |
| 190 | SimpleTensor<float> dst_tmp = pixel_wise_multiplication<float, float, float>(src1_tmp, src2_tmp, scale, convert_policy, rounding_policy, DataType::F32, qout); |
| 191 | dst = convert_to_symmetric<int16_t>(dst_tmp, qout); |
| 192 | } |
| 193 | else |
| 194 | { |
| 195 | if(scale < 0) |
| 196 | { |
| 197 | ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative"); |
| 198 | } |
| 199 | |
| 200 | Coordinates id_src1{}; |
| 201 | Coordinates id_src2{}; |
| 202 | Coordinates id_dst{}; |
| 203 | BroadcastUnroll<Coordinates::num_max_dimensions>::unroll(src1, src2, dst, scale, convert_policy, rounding_policy, id_src1, id_src2, id_dst); |
| 204 | } |
| 205 | return dst; |
| 206 | } |
| 207 | |
| 208 | template <> |
Pablo Tello | 52ea9c2 | 2019-12-10 11:28:53 +0000 | [diff] [blame] | 209 | SimpleTensor<int8_t> pixel_wise_multiplication(const SimpleTensor<int8_t> &src1, const SimpleTensor<int8_t> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 210 | DataType dt_out, const QuantizationInfo &qout) |
Pablo Tello | 52ea9c2 | 2019-12-10 11:28:53 +0000 | [diff] [blame] | 211 | { |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 212 | SimpleTensor<int8_t> dst(TensorShape::broadcast_shape(src1.shape(), src2.shape()), dt_out, 1, qout); |
Pablo Tello | 52ea9c2 | 2019-12-10 11:28:53 +0000 | [diff] [blame] | 213 | |
| 214 | if(src1.data_type() == DataType::QASYMM8_SIGNED && src2.data_type() == DataType::QASYMM8_SIGNED) |
| 215 | { |
| 216 | SimpleTensor<float> src1_tmp = convert_from_asymmetric(src1); |
| 217 | SimpleTensor<float> src2_tmp = convert_from_asymmetric(src2); |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 218 | SimpleTensor<float> dst_tmp = pixel_wise_multiplication<float, float, float>(src1_tmp, src2_tmp, scale, convert_policy, rounding_policy, DataType::F32, qout); |
Pablo Tello | 52ea9c2 | 2019-12-10 11:28:53 +0000 | [diff] [blame] | 219 | dst = convert_to_asymmetric<int8_t>(dst_tmp, qout); |
| 220 | } |
| 221 | else |
| 222 | { |
| 223 | if(scale < 0) |
| 224 | { |
| 225 | ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative"); |
| 226 | } |
| 227 | |
| 228 | Coordinates id_src1{}; |
| 229 | Coordinates id_src2{}; |
| 230 | Coordinates id_dst{}; |
| 231 | BroadcastUnroll<Coordinates::num_max_dimensions>::unroll(src1, src2, dst, scale, convert_policy, rounding_policy, id_src1, id_src2, id_dst); |
| 232 | } |
| 233 | return dst; |
| 234 | } |
| 235 | |
| 236 | template <> |
Michele Di Giorgio | d8a468f | 2019-06-19 15:34:41 +0100 | [diff] [blame] | 237 | SimpleTensor<int16_t> pixel_wise_multiplication(const SimpleTensor<int16_t> &src1, const SimpleTensor<int16_t> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 238 | DataType dt_out, const QuantizationInfo &qout) |
Michele Di Giorgio | d8a468f | 2019-06-19 15:34:41 +0100 | [diff] [blame] | 239 | { |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 240 | SimpleTensor<int16_t> dst(TensorShape::broadcast_shape(src1.shape(), src2.shape()), dt_out, 1, qout); |
Michele Di Giorgio | d8a468f | 2019-06-19 15:34:41 +0100 | [diff] [blame] | 241 | |
| 242 | if(src1.data_type() == DataType::QSYMM16 && src2.data_type() == DataType::QSYMM16) |
| 243 | { |
| 244 | SimpleTensor<float> src1_tmp = convert_from_symmetric<int16_t>(src1); |
| 245 | SimpleTensor<float> src2_tmp = convert_from_symmetric<int16_t>(src2); |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 246 | SimpleTensor<float> dst_tmp = pixel_wise_multiplication<float, float, float>(src1_tmp, src2_tmp, scale, convert_policy, rounding_policy, DataType::F32, qout); |
Michele Di Giorgio | d8a468f | 2019-06-19 15:34:41 +0100 | [diff] [blame] | 247 | dst = convert_to_symmetric<int16_t>(dst_tmp, qout); |
| 248 | } |
| 249 | else |
| 250 | { |
| 251 | if(scale < 0) |
| 252 | { |
| 253 | ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative"); |
| 254 | } |
| 255 | |
| 256 | Coordinates id_src1{}; |
| 257 | Coordinates id_src2{}; |
| 258 | Coordinates id_dst{}; |
| 259 | BroadcastUnroll<Coordinates::num_max_dimensions>::unroll(src1, src2, dst, scale, convert_policy, rounding_policy, id_src1, id_src2, id_dst); |
| 260 | } |
| 261 | return dst; |
| 262 | } |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 263 | // *INDENT-OFF* |
| 264 | // clang-format off |
Michele Di Giorgio | 9428a18 | 2020-03-30 14:10:20 +0100 | [diff] [blame] | 265 | 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, DataType dt_out, const QuantizationInfo &qout); |
| 266 | template SimpleTensor<int32_t> pixel_wise_multiplication(const SimpleTensor<int16_t> &src1, const SimpleTensor<int16_t> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, DataType dt_out, const QuantizationInfo &qout); |
| 267 | template SimpleTensor<float> pixel_wise_multiplication(const SimpleTensor<float> &src1, const SimpleTensor<float> &src2, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy, DataType dt_out, const QuantizationInfo &qout); |
| 268 | 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, DataType dt_out, const QuantizationInfo &qout); |
John Richardson | dd715f2 | 2017-09-18 16:10:48 +0100 | [diff] [blame] | 269 | // clang-format on |
| 270 | // *INDENT-ON* |
| 271 | } // namespace reference |
| 272 | } // namespace validation |
| 273 | } // namespace test |
| 274 | } // namespace arm_compute |