| 128 | } |
| 129 | |
| 130 | TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { |
| 131 | auto* params = |
| 132 | reinterpret_cast<TfLiteFullyConnectedParams*>(node->builtin_data); |
| 133 | OpData* data = reinterpret_cast<OpData*>(node->user_data); |
| 134 | |
| 135 | // Check we have all the inputs and outputs we need. |
| 136 | TF_LITE_ENSURE(context, node->inputs->size == 2 || node->inputs->size == 3); |
| 137 | // Shuffled formats need a workspace to store the shuffled input activations. |
| 138 | const int expected_outputs_count = |
| 139 | params->weights_format == kTfLiteFullyConnectedWeightsFormatDefault ? 1 |
| 140 | : 2; |
| 141 | TF_LITE_ENSURE_EQ(context, node->outputs->size, expected_outputs_count); |
| 142 | |
| 143 | const TfLiteTensor* input = GetInput(context, node, kInputTensor); |
| 144 | const TfLiteTensor* filter = GetInput(context, node, kWeightsTensor); |
| 145 | const TfLiteTensor* bias = |
| 146 | (node->inputs->size == 3) |
| 147 | ? GetOptionalInputTensor(context, node, kBiasTensor) |
| 148 | : nullptr; |
| 149 | TfLiteTensor* output = GetOutput(context, node, kOutputTensor); |
| 150 | |
| 151 | // Check proper datatype match among all Input Tensors |
| 152 | TF_LITE_ENSURE_STATUS( |
| 153 | CheckTypes(context, input, filter, bias, output, params)); |
| 154 | |
| 155 | // Check all the parameters of tensor match within themselves and match the |
| 156 | // input configuration. |
| 157 | int input_size = 1; |
| 158 | for (int i = 0; i < input->dims->size; i++) { |
| 159 | input_size *= input->dims->data[i]; |
| 160 | } |
| 161 | |
| 162 | TF_LITE_ENSURE_EQ(context, NumDimensions(filter), 2); |
| 163 | const int batch_size = input_size / filter->dims->data[1]; |
| 164 | const int num_units = filter->dims->data[0]; |
| 165 | |
| 166 | if (bias) { |
| 167 | TF_LITE_ENSURE_EQ(context, NumElements(bias), SizeOfDimension(filter, 0)); |
| 168 | } |
| 169 | |
| 170 | // Note that quantized inference requires that all tensors have their |
| 171 | // parameters set. This is usually done during quantized training. |
| 172 | if (input->type == kTfLiteUInt8 || input->type == kTfLiteInt8) { |
| 173 | double real_multiplier = 0.0; |
| 174 | TF_LITE_ENSURE_STATUS(GetQuantizedConvolutionMultipler( |
| 175 | context, input, filter, bias, output, &real_multiplier)); |
| 176 | int exponent; |
| 177 | QuantizeMultiplier(real_multiplier, &data->output_multiplier, &exponent); |
| 178 | data->output_shift = exponent; |
| 179 | TF_LITE_ENSURE_STATUS(CalculateActivationRangeQuantized( |
| 180 | context, params->activation, output, &data->output_activation_min, |
| 181 | &data->output_activation_max)); |
| 182 | } |
| 183 | |
| 184 | // If we have to perform on-the-fly quantization (with quantized weights and |
| 185 | // float inputs) first we need to quantize the inputs. Allocate a temporary |
| 186 | // buffer to store the intermediate quantized values. |
| 187 | if (input->type == kTfLiteFloat32 && |
nothing calls this directly
no test coverage detected