| 193 | } |
| 194 | |
| 195 | TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { |
| 196 | auto* params = reinterpret_cast<TfLiteSVDFParams*>(node->builtin_data); |
| 197 | OpData* op_data = reinterpret_cast<OpData*>(node->user_data); |
| 198 | |
| 199 | const TfLiteTensor* input = GetInput(context, node, kInputTensor); |
| 200 | const TfLiteTensor* weights_feature = |
| 201 | GetInput(context, node, kWeightsFeatureTensor); |
| 202 | const TfLiteTensor* weights_time = |
| 203 | GetInput(context, node, kWeightsTimeTensor); |
| 204 | const TfLiteTensor* bias = GetOptionalInputTensor(context, node, kBiasTensor); |
| 205 | |
| 206 | TfLiteTensor* scratch = GetTemporary(context, node, /*index=*/0); |
| 207 | |
| 208 | TfLiteTensor* activation_state = |
| 209 | &context->tensors[op_data->activation_state_tensor_index]; |
| 210 | TfLiteTensor* output = GetOutput(context, node, kOutputTensor); |
| 211 | |
| 212 | switch (weights_feature->type) { |
| 213 | case kTfLiteFloat32: { |
| 214 | reference_ops::EvalFloatSVDF(context, node, input, weights_feature, |
| 215 | weights_time, bias, params, scratch, |
| 216 | activation_state, output); |
| 217 | return kTfLiteOk; |
| 218 | break; |
| 219 | } |
| 220 | case kTfLiteUInt8: |
| 221 | case kTfLiteInt8: { |
| 222 | TfLiteTensor* input_quantized = GetTemporary(context, node, /*index=*/1); |
| 223 | TfLiteTensor* scaling_factors = GetTemporary(context, node, /*index=*/2); |
| 224 | TfLiteTensor* float_weights_time = |
| 225 | GetTemporary(context, node, /*index=*/3); |
| 226 | |
| 227 | // Dequantize weights time. |
| 228 | // TODO(alanchiao): this dequantization initialization only needs to |
| 229 | // happen once per model and should theoretically be placed in either Init |
| 230 | // or Prepare. However, TFLite doesn't allocate float_weights_time until |
| 231 | // the Eval function. |
| 232 | // TODO(alanchiao): refactor logic out into dequantize function. |
| 233 | if (!op_data->float_weights_time_initialized) { |
| 234 | const float dequantization_scale = weights_time->params.scale; |
| 235 | const int8_t* weights_time_ptr; |
| 236 | if (weights_feature->type == kTfLiteUInt8) { |
| 237 | weights_time_ptr = reinterpret_cast<const int8_t*>( |
| 238 | GetTensorData<uint8_t>(weights_time)); |
| 239 | } else { |
| 240 | weights_time_ptr = GetTensorData<int8_t>(weights_time); |
| 241 | } |
| 242 | float* float_weights_time_ptr = |
| 243 | GetTensorData<float>(float_weights_time); |
| 244 | for (int i = 0; i < NumElements(float_weights_time); ++i) { |
| 245 | float_weights_time_ptr[i] = |
| 246 | weights_time_ptr[i] * dequantization_scale; |
| 247 | } |
| 248 | op_data->float_weights_time_initialized = true; |
| 249 | } |
| 250 | reference_ops::EvalHybridSVDF(context, node, input, weights_feature, |
| 251 | float_weights_time, bias, params, scratch, |
| 252 | scaling_factors, input_quantized, |
nothing calls this directly
no test coverage detected