Rickard Bolin | bc6ee58 | 2022-11-04 08:24:29 +0000 | [diff] [blame^] | 1 | # SPDX-FileCopyrightText: Copyright 2020 Arm Limited and/or its affiliates <open-source-office@arm.com> |
Tim Hall | 79d07d2 | 2020-04-27 18:20:16 +0100 | [diff] [blame] | 2 | # |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the License); you may |
| 6 | # not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an AS IS BASIS, WITHOUT |
| 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
Rickard Bolin | bc6ee58 | 2022-11-04 08:24:29 +0000 | [diff] [blame^] | 16 | # |
Tim Hall | 79d07d2 | 2020-04-27 18:20:16 +0100 | [diff] [blame] | 17 | # Description: |
| 18 | # Contains various scaling calculations for weights, elementwise operations, pooling etc. |
Tim Hall | 79d07d2 | 2020-04-27 18:20:16 +0100 | [diff] [blame] | 19 | import math |
Tim Hall | 79d07d2 | 2020-04-27 18:20:16 +0100 | [diff] [blame] | 20 | from enum import IntEnum |
| 21 | |
Diego Russo | ea6111a | 2020-04-14 18:41:58 +0100 | [diff] [blame] | 22 | from .numeric_util import round_away_zero |
| 23 | |
Tim Hall | 79d07d2 | 2020-04-27 18:20:16 +0100 | [diff] [blame] | 24 | |
| 25 | class OperandToScale(IntEnum): |
| 26 | OPa = 1 |
| 27 | OPb = 2 |
| 28 | |
| 29 | |
| 30 | # Quantise floating point scale value into 32-bit int scale and 6-bit shift |
| 31 | def quantise_scale(scale): |
| 32 | significand, exponent = math.frexp(scale) |
| 33 | significand_q31 = int(round_away_zero(significand * (1 << 31))) |
| 34 | exponent_q31 = exponent - 31 |
| 35 | shift = exponent_q31 * -1 |
| 36 | |
Jacob Bohlin | 1cdc467 | 2020-08-20 15:51:37 +0200 | [diff] [blame] | 37 | if not (0 <= shift < (1 << 6)): |
Tim Hall | 79d07d2 | 2020-04-27 18:20:16 +0100 | [diff] [blame] | 38 | # Shift outside of valid range, set scale to 0 |
| 39 | return 0, 16 |
| 40 | |
| 41 | return significand_q31, shift |
| 42 | |
| 43 | |
Fredrik Svedberg | d67c0aa | 2020-03-30 13:15:28 +0200 | [diff] [blame] | 44 | # Reduced precision quantization for int16 |
| 45 | def reduced_quantise_scale(scale): |
| 46 | multiplier, shift = quantise_scale(scale) |
Fredrik Svedberg | d2e3355 | 2020-09-01 15:42:22 +0200 | [diff] [blame] | 47 | reduced_multiplier = int((multiplier + (1 << 15)) >> 16) if multiplier < 32767 << 16 else 32767 |
Fredrik Svedberg | d67c0aa | 2020-03-30 13:15:28 +0200 | [diff] [blame] | 48 | reduced_shift = shift - 16 |
| 49 | |
Jacob Bohlin | c3c08d8 | 2020-08-31 10:14:02 +0200 | [diff] [blame] | 50 | if not (0 <= shift < (1 << 6)): |
| 51 | # Shift outside of valid range, set scale to 0 |
| 52 | return 0, 16 |
| 53 | |
Fredrik Svedberg | d67c0aa | 2020-03-30 13:15:28 +0200 | [diff] [blame] | 54 | return reduced_multiplier, reduced_shift |
| 55 | |
| 56 | |
Tim Hall | 79d07d2 | 2020-04-27 18:20:16 +0100 | [diff] [blame] | 57 | # Calculate global OFM scale for Average Pooling |
| 58 | def quantise_pooling_scale(nr_kernel_elements, rescale_bits=0): |
| 59 | _, k = math.frexp(nr_kernel_elements - 1) |
| 60 | N = 31 - rescale_bits |
| 61 | scale = ((1 << (N + k)) + (1 << k)) // nr_kernel_elements |
| 62 | shift = N + k |
| 63 | |
| 64 | assert shift < (1 << 6) |
| 65 | |
| 66 | return scale, shift |
| 67 | |
| 68 | |
| 69 | # Calculate elementwise Mul OFM scale+shift |
| 70 | def elementwise_mul_scale(input_scale, input2_scale, output_scale): |
| 71 | output_rescale = (input_scale * input2_scale) / output_scale |
| 72 | out_scale, out_shift = quantise_scale(output_rescale) |
| 73 | return out_scale, out_shift |
| 74 | |
| 75 | |
| 76 | # Simplified version of calculating elementwise Add/Sub scales |
| 77 | def simplified_elementwise_add_sub_scale(input1_scale, input2_scale, output_scale, input_shift=16): |
| 78 | max_input_scale = max(input1_scale, input2_scale) |
| 79 | |
| 80 | input1_rescale = input1_scale * (1 << input_shift) / (2 * max_input_scale) |
| 81 | input2_rescale = input2_scale * (1 << input_shift) / (2 * max_input_scale) |
| 82 | output_rescale = (2 * max_input_scale) / (output_scale * (1 << input_shift)) |
| 83 | |
| 84 | out_scale, out_shift = quantise_scale(output_rescale) |
| 85 | |
| 86 | return input1_rescale, input2_rescale, out_scale, out_shift |
| 87 | |
| 88 | |
| 89 | # Advanced version of calculating elementwise Add/Sub scales |
| 90 | def advanced_elementwise_add_sub_scale(input1_scale, input2_scale, output_scale, bitdepth): |
| 91 | # Always scale the smaller of the input scales |
| 92 | max_input_scale = max(input1_scale, input2_scale) |
| 93 | min_input_scale = min(input1_scale, input2_scale) |
Fredrik Svedberg | c91dd1c | 2020-05-04 15:40:04 +0200 | [diff] [blame] | 94 | input_shift = 20 if bitdepth == 8 else 15 |
Tim Hall | 79d07d2 | 2020-04-27 18:20:16 +0100 | [diff] [blame] | 95 | op_to_scale = OperandToScale.OPa if input1_scale < input2_scale else OperandToScale.OPb |
| 96 | |
| 97 | input1_rescale, _, out_scale, out_shift = simplified_elementwise_add_sub_scale( |
| 98 | min_input_scale, max_input_scale, output_scale, input_shift |
| 99 | ) |
| 100 | |
| 101 | in_scale, in_shift = quantise_scale(input1_rescale) |
| 102 | |
| 103 | return in_scale, in_shift, out_scale, out_shift, op_to_scale |