1# SPDX-FileCopyrightText: Copyright 2010-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> 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. 16# 17from test_settings import TestSettings 18 19import tensorflow as tf 20import numpy as np 21import tf_keras as keras 22 23 24class AddMulSettings(TestSettings): 25 26 def __init__(self, 27 dataset, 28 testtype, 29 regenerate_weights, 30 regenerate_input, 31 regenerate_biases, 32 schema_file, 33 channels=1, 34 x_in=4, 35 y_in=4, 36 decimal_input=6, 37 randmin=TestSettings.INT8_MIN, 38 randmax=TestSettings.INT8_MAX, 39 out_activation_min=TestSettings.INT8_MIN, 40 out_activation_max=TestSettings.INT8_MAX, 41 int16xint8=False, 42 interpreter="tensorflow"): 43 super().__init__(dataset, 44 testtype, 45 regenerate_weights, 46 regenerate_input, 47 regenerate_biases, 48 schema_file, 49 in_ch=channels, 50 out_ch=channels, 51 x_in=x_in, 52 y_in=y_in, 53 w_x=1, 54 w_y=1, 55 stride_x=1, 56 stride_y=1, 57 pad=False, 58 randmin=randmin, 59 randmax=randmax, 60 batches=1, 61 generate_bias=False, 62 relu6=False, 63 out_activation_min=out_activation_min, 64 out_activation_max=out_activation_max, 65 int16xint8=int16xint8, 66 interpreter=interpreter) 67 68 self.x_input = self.x_output = x_in 69 self.y_input = self.y_output = y_in 70 self.decimal_input = decimal_input 71 72 self.left_shift = 15 if self.is_int16xint8 else 20 73 74 def generate_data(self, input_data1=None, input_data2=None) -> None: 75 input_shape = (1, self.y_input, self.x_input, self.input_ch) 76 77 input_data1 = self.get_randomized_data(list(input_shape), 78 self.inputs_table_file, 79 regenerate=self.regenerate_new_input, 80 decimals=self.decimal_input) 81 input_data2 = self.get_randomized_data(list(input_shape), 82 self.kernel_table_file, 83 regenerate=self.regenerate_new_weights, 84 decimals=self.decimal_input) 85 86 if self.is_int16xint8: 87 inttype = "int16_t" 88 inttype_tf = tf.int16 89 else: 90 inttype = "int8_t" 91 inttype_tf = tf.int8 92 93 # Create a one-layer functional Keras model as add/mul cannot use a sequntial Keras model. 94 input1 = keras.layers.Input(shape=input_shape[1:]) 95 input2 = keras.layers.Input(shape=input_shape[1:]) 96 if self.test_type == 'add': 97 layer = keras.layers.Add()([input1, input2]) 98 elif self.test_type == 'mul': 99 layer = keras.layers.Multiply()([input1, input2]) 100 else: 101 raise RuntimeError("Wrong test type") 102 out = keras.layers.Lambda(function=lambda x: x)(layer) 103 model = keras.models.Model(inputs=[input1, input2], outputs=out) 104 105 interpreter = self.convert_and_interpret(model, inttype_tf) 106 107 input_details = interpreter.get_input_details() 108 interpreter.set_tensor(input_details[0]["index"], tf.cast(input_data1, inttype_tf)) 109 interpreter.set_tensor(input_details[1]["index"], tf.cast(input_data2, inttype_tf)) 110 111 # Calculate multipliers, shifts and offsets. 112 (input1_scale, self.input1_zero_point) = input_details[0]['quantization'] 113 (input2_scale, self.input2_zero_point) = input_details[1]['quantization'] 114 self.input1_zero_point = -self.input1_zero_point 115 self.input2_zero_point = -self.input2_zero_point 116 double_max_input_scale = max(input1_scale, input2_scale) * 2 117 (self.input1_mult, self.input1_shift) = self.quantize_scale(input1_scale / double_max_input_scale) 118 (self.input2_mult, self.input2_shift) = self.quantize_scale(input2_scale / double_max_input_scale) 119 120 if self.test_type == 'add': 121 actual_output_scale = double_max_input_scale / ((1 << self.left_shift) * self.output_scale) 122 elif self.test_type == 'mul': 123 actual_output_scale = input1_scale * input2_scale / self.output_scale 124 (self.output_mult, self.output_shift) = self.quantize_scale(actual_output_scale) 125 126 # Generate reference. 127 interpreter.invoke() 128 output_details = interpreter.get_output_details() 129 output_data = interpreter.get_tensor(output_details[0]["index"]) 130 self.generate_c_array("input1", input_data1, datatype=inttype) 131 self.generate_c_array("input2", input_data2, datatype=inttype) 132 self.generate_c_array(self.output_data_file_prefix, 133 np.clip(output_data, self.out_activation_min, self.out_activation_max), 134 datatype=inttype) 135 136 self.write_c_config_header() 137 self.write_c_header_wrapper() 138 139 def write_c_config_header(self) -> None: 140 super().write_c_config_header(write_common_parameters=False) 141 142 filename = self.config_data 143 filepath = self.headers_dir + filename 144 prefix = self.testdataset.upper() 145 146 with open(filepath, "a") as f: 147 f.write("#define {}_DST_SIZE {}\n".format(prefix, 148 self.batches * self.y_input * self.x_input * self.input_ch)) 149 f.write("#define {}_OUT_ACTIVATION_MIN {}\n".format(prefix, self.out_activation_min)) 150 f.write("#define {}_OUT_ACTIVATION_MAX {}\n".format(prefix, self.out_activation_max)) 151 f.write("#define {}_INPUT1_OFFSET {}\n".format(prefix, self.input1_zero_point)) 152 f.write("#define {}_INPUT2_OFFSET {}\n".format(prefix, self.input2_zero_point)) 153 f.write("#define {}_OUTPUT_MULT {}\n".format(prefix, self.output_mult)) 154 f.write("#define {}_OUTPUT_SHIFT {}\n".format(prefix, self.output_shift)) 155 f.write("#define {}_OUTPUT_OFFSET {}\n".format(prefix, self.output_zero_point)) 156 if self.test_type == 'add': 157 f.write("#define {}_LEFT_SHIFT {}\n".format(prefix, self.left_shift)) 158 f.write("#define {}_INPUT1_SHIFT {}\n".format(prefix, self.input1_shift)) 159 f.write("#define {}_INPUT2_SHIFT {}\n".format(prefix, self.input2_shift)) 160 f.write("#define {}_INPUT1_MULT {}\n".format(prefix, self.input1_mult)) 161 f.write("#define {}_INPUT2_MULT {}\n".format(prefix, self.input2_mult)) 162