Quantize the op input. Will increment op_idx if ops are added.
| 362 | |
| 363 | // Quantize the op input. Will increment op_idx if ops are added. |
| 364 | TfLiteStatus QuantizeOpInput( |
| 365 | ModelT* model, int32_t subgraph_idx, size_t* op_idx, |
| 366 | operator_property::OperatorProperty property, |
| 367 | const std::pair<int32_t, operator_property::TensorProperty>& input, |
| 368 | ErrorReporter* error_reporter) { |
| 369 | int32_t input_idx = input.first; |
| 370 | operator_property::TensorProperty tensor_property = input.second; |
| 371 | SubGraphT* subgraph = model->subgraphs.at(subgraph_idx).get(); |
| 372 | OperatorT* op = subgraph->operators[*op_idx].get(); |
| 373 | const BuiltinOperator op_code = |
| 374 | model->operator_codes[op->opcode_index]->builtin_code; |
| 375 | if (input_idx >= op->inputs.size()) { |
| 376 | error_reporter->Report( |
| 377 | "Required input index %d is larger than the input length of op " |
| 378 | "%s at index %d in subgraph %d", |
| 379 | input_idx, op->inputs.size(), EnumNameBuiltinOperator(op_code), *op_idx, |
| 380 | subgraph_idx); |
| 381 | return kTfLiteError; |
| 382 | } |
| 383 | const int32_t tensor_idx = op->inputs[input_idx]; |
| 384 | TensorT* tensor = subgraph->tensors[tensor_idx].get(); |
| 385 | const bool is_input_quantized = utils::QuantizationParametersExist(tensor); |
| 386 | if (property.quantizable && !is_input_quantized) { |
| 387 | // The operation is quantizable, but the input isn't yet quantized. |
| 388 | if (utils::HasBuffer(model, subgraph, tensor_idx)) { |
| 389 | // TODO(suharshs): Look at consumers, throw error if one consumer is |
| 390 | // per-channel and one per-layer. |
| 391 | if (utils::QuantizeWeight(model, tensor, tensor_property.per_axis, |
| 392 | tensor_property.per_axis_index) == |
| 393 | kTfLiteError) { |
| 394 | error_reporter->Report( |
| 395 | "Unable to quantize buffer or min/max value for input %d " |
| 396 | "in op %s in subgraph %d, node: %d", |
| 397 | input_idx, EnumNameBuiltinOperator(op_code), subgraph_idx, *op_idx); |
| 398 | return kTfLiteError; |
| 399 | } |
| 400 | } else if (utils::HasMinMax(tensor)) { |
| 401 | // TODO(suharshs): Handle per-channel dynamic tensor. |
| 402 | if (IsSubgraphInput(subgraph, tensor_idx)) { |
| 403 | utils::QuantizeActivation(tensor); |
| 404 | } else { |
| 405 | // If the tensor is not a model input, we need to add a Quantize |
| 406 | // operation since the preceding op may require a float output. |
| 407 | std::unique_ptr<TensorT> op_output; |
| 408 | utils::MakeTensor(tensor->name + "_int8", tensor->shape, |
| 409 | TensorType_INT8, &op_output); |
| 410 | op_output->quantization = absl::make_unique<QuantizationParametersT>(); |
| 411 | op_output->quantization->min.push_back(tensor->quantization->min[0]); |
| 412 | op_output->quantization->max.push_back(tensor->quantization->max[0]); |
| 413 | utils::QuantizeActivation(op_output.get()); |
| 414 | const int32_t quant_op_output_idx = subgraph->tensors.size(); |
| 415 | subgraph->tensors.push_back(std::move(op_output)); |
| 416 | std::unique_ptr<OperatorT> quant_op; |
| 417 | utils::MakeQuantizeOperator(model, &quant_op, tensor_idx, |
| 418 | quant_op_output_idx); |
| 419 | subgraph->operators.insert(subgraph->operators.begin() + *op_idx, |
| 420 | std::move(quant_op)); |
| 421 | op->inputs[input_idx] = quant_op_output_idx; |
no test coverage detected