| 852 | // important to show the clamping behavior we want in particular. |
| 853 | template <typename T1, typename T2, typename T3> |
| 854 | void QuantizedAdd(const Eigen::ThreadPoolDevice& device, const Tensor& input, |
| 855 | float input_min, float input_max, const Tensor& smaller_input, |
| 856 | float smaller_input_min, float smaller_input_max, |
| 857 | Tensor* output, float* output_min, float* output_max) { |
| 858 | const auto& input_flat = input.flat<T1>(); |
| 859 | const auto& smaller_input_flat = smaller_input.flat<T2>(); |
| 860 | auto output_flat = output->flat<T3>(); |
| 861 | |
| 862 | GetOutputMinAndMaxForQuantizedAdd(input_min, input_max, smaller_input_min, |
| 863 | smaller_input_max, output_min, output_max); |
| 864 | // To do addition properly, we need to compensate for a possibly unbalanced |
| 865 | // zero point in the total representation. The quantized value that |
| 866 | // represents the real number zero needs to be subtracted before addition to |
| 867 | // make sure that the identity of zero + zero = zero holds. |
| 868 | const T3 zero_in_total_space = |
| 869 | FloatToQuantized<T3>(0.0f, *output_min, *output_max); |
| 870 | |
| 871 | const int64 input_element_count = input.NumElements(); |
| 872 | const int64 smaller_input_element_count = smaller_input.NumElements(); |
| 873 | |
| 874 | float total_min = *output_min; |
| 875 | float total_max = *output_max; |
| 876 | const size_t how_many_iterations = |
| 877 | (input_element_count / smaller_input_element_count); |
| 878 | for (size_t iteration = 0; iteration < how_many_iterations; ++iteration) { |
| 879 | const size_t offset = iteration * smaller_input_element_count; |
| 880 | for (int c = 0; c < smaller_input_element_count; ++c) { |
| 881 | const int index = (offset + c); |
| 882 | // The two numbers we're going to add can each be in very different |
| 883 | // ranges (e.g. the quantized value '127' may represent very different |
| 884 | // real numbers in both) so we need to convert them to a common range |
| 885 | // before we sum them. |
| 886 | const T1 input_value = input_flat(index); |
| 887 | const T3 input_in_total_space = RequantizeInNewRange<T1, T3>( |
| 888 | input_value, input_min, input_max, total_min, total_max); |
| 889 | const T2 smaller_input_value = smaller_input_flat(c); |
| 890 | const T3 smaller_input_in_total_space = |
| 891 | RequantizeInNewRange<T2, T3>(smaller_input_value, smaller_input_min, |
| 892 | smaller_input_max, total_min, total_max); |
| 893 | const T3 total_pre = input_in_total_space + smaller_input_in_total_space; |
| 894 | // As noted above, we need to compensate for the offset of the actual |
| 895 | // zero point in the space we're operating in. |
| 896 | const T3 total = total_pre + zero_in_total_space; |
| 897 | output_flat(index) = total; |
| 898 | } |
| 899 | } |
| 900 | } |
| 901 | |
| 902 | // See gemmlowp/internal/multi_thread_gemm.h for the semantics of Execute. |
| 903 | class TensorflowGemmlowpWorkersPool { |