| 647 | } |
| 648 | |
| 649 | tensorflow::Status ConvertUnsupportedOperator( |
| 650 | const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, |
| 651 | const ModelFlags& model_flags, Model* model) { |
| 652 | // Names of special attributes in TF graph that are used by Toco. |
| 653 | static constexpr char kAttrOutputQuantized[] = "_output_quantized"; |
| 654 | static constexpr char kAttrOutputTypes[] = "_output_types"; |
| 655 | static constexpr char kAttrOutputShapes[] = "_output_shapes"; |
| 656 | static constexpr char kAttrSupportOutputTypeFloatInQuantizedOp[] = |
| 657 | "_support_output_type_float_in_quantized_op"; |
| 658 | |
| 659 | LOG(INFO) << "Converting unsupported operation: " << node.op(); |
| 660 | |
| 661 | auto* op = new TensorFlowUnsupportedOperator; |
| 662 | op->tensorflow_op = node.op(); |
| 663 | |
| 664 | // For Flex mode. Please read the comments of the function. |
| 665 | RetainTensorFlowNodeDef(node, op); |
| 666 | |
| 667 | model->operators.emplace_back(op); |
| 668 | |
| 669 | // Parse inputs. |
| 670 | const int num_inputs = GetInputsCount(node, tf_import_flags); |
| 671 | for (int i = 0; i < num_inputs; ++i) { |
| 672 | op->inputs.push_back(node.input(i)); |
| 673 | } |
| 674 | |
| 675 | // Parse outputs. Name them after the node's name, plus an ordinal suffix. |
| 676 | // Note that some outputs are to be multiplied by a named attribute. |
| 677 | const tensorflow::OpDef* op_def = nullptr; |
| 678 | if (tensorflow::OpRegistry::Global()->LookUpOpDef(node.op(), &op_def).ok()) { |
| 679 | GetOutputNamesFromNodeDef(node, *op_def, op); |
| 680 | } else { |
| 681 | op->outputs.push_back(node.name()); // Implicit :0. |
| 682 | } |
| 683 | |
| 684 | // Parse if the op supports quantization |
| 685 | if (HasAttr(node, kAttrOutputQuantized)) { |
| 686 | op->quantized = GetBoolAttr(node, kAttrOutputQuantized); |
| 687 | } |
| 688 | // Parse if the quantized op allows output arrays of type float |
| 689 | if (HasAttr(node, kAttrSupportOutputTypeFloatInQuantizedOp)) { |
| 690 | op->support_output_type_float_in_quantized_op = |
| 691 | GetBoolAttr(node, kAttrSupportOutputTypeFloatInQuantizedOp); |
| 692 | } |
| 693 | |
| 694 | // Parse output type(s). |
| 695 | if (HasAttr(node, kAttrOutputTypes)) { |
| 696 | const auto& output_types = GetListAttr(node, kAttrOutputTypes); |
| 697 | for (int i = 0; i < output_types.type_size(); ++i) { |
| 698 | op->output_data_types.push_back(ConvertDataType(output_types.type(i))); |
| 699 | } |
| 700 | } else if (HasAttr(node, "Tout")) { |
| 701 | const auto& output_type = GetDataTypeAttr(node, "Tout"); |
| 702 | op->output_data_types.push_back(ConvertDataType(output_type)); |
| 703 | } else if (op_def != nullptr) { |
| 704 | GetOutputTypesFromNodeDef(node, *op_def, op); |
| 705 | } else { |
| 706 | // TODO(b/113613439): Figure out how to propagate types for custom ops |
no test coverage detected