| 98 | } |
| 99 | |
| 100 | void ImportOperators( |
| 101 | const ::tflite::Model& input_model, |
| 102 | const std::map<string, std::unique_ptr<BaseOperator>>& ops_by_name, |
| 103 | const details::TensorsTable& tensors_table, |
| 104 | const details::OperatorsTable& operators_table, Model* model) { |
| 105 | // TODO(aselle): add support for multiple subgraphs. |
| 106 | auto ops = (*input_model.subgraphs())[0]->operators(); |
| 107 | |
| 108 | if (!ops) return; |
| 109 | for (const auto* input_op : *ops) { |
| 110 | int index = input_op->opcode_index(); |
| 111 | if (index < 0 || index > operators_table.size()) { |
| 112 | LOG(FATAL) << "Index " << index << " must be between zero and " |
| 113 | << operators_table.size(); |
| 114 | } |
| 115 | string opname = operators_table.at(index); |
| 116 | |
| 117 | // Find and use the appropriate operator deserialization factory. |
| 118 | std::unique_ptr<Operator> new_op = nullptr; |
| 119 | if (ops_by_name.count(opname) == 0) { |
| 120 | string effective_opname = "TENSORFLOW_UNSUPPORTED"; |
| 121 | if (ops_by_name.count(effective_opname) == 0) { |
| 122 | LOG(FATAL) << "Internal logic error: TENSORFLOW_UNSUPPORTED not found."; |
| 123 | } |
| 124 | new_op = ops_by_name.at(effective_opname) |
| 125 | ->Deserialize(input_op->builtin_options(), |
| 126 | input_op->custom_options()); |
| 127 | if (new_op->type == OperatorType::kUnsupported) { |
| 128 | auto* unsupported_op = |
| 129 | static_cast<TensorFlowUnsupportedOperator*>(new_op.get()); |
| 130 | unsupported_op->tensorflow_op = opname; |
| 131 | // TODO(b/109932940): Remove this when quantized is removed. |
| 132 | // For now, we assume all ops are quantized. |
| 133 | unsupported_op->quantized = true; |
| 134 | } else { |
| 135 | LOG(FATAL) << "Expected a TensorFlowUnsupportedOperator"; |
| 136 | } |
| 137 | } else { |
| 138 | new_op = ops_by_name.at(opname)->Deserialize(input_op->builtin_options(), |
| 139 | input_op->custom_options()); |
| 140 | } |
| 141 | model->operators.emplace_back(new_op.release()); |
| 142 | auto* op = model->operators.back().get(); |
| 143 | |
| 144 | // Make sure all the inputs and outputs are hooked up. |
| 145 | auto inputs = input_op->inputs(); |
| 146 | for (int i = 0; i < inputs->Length(); i++) { |
| 147 | auto input_index = inputs->Get(i); |
| 148 | // input_index == -1 indicates optional tensor. |
| 149 | if (input_index != -1) { |
| 150 | const string& input_name = tensors_table.at(input_index); |
| 151 | op->inputs.push_back(input_name); |
| 152 | } else { |
| 153 | const string& tensor_name = |
| 154 | toco::AvailableArrayName(*model, "OptionalTensor"); |
| 155 | model->CreateOptionalArray(tensor_name); |
| 156 | op->inputs.push_back(tensor_name); |
| 157 | } |
no test coverage detected