Performs basic consistency checks on a sub-graph.
| 208 | |
| 209 | // Performs basic consistency checks on a sub-graph. |
| 210 | bool VerifySubGraphConsistency(const Model& model, const SubGraph& subgraph, |
| 211 | ErrorReporter* error_reporter) { |
| 212 | absl::flat_hash_set<int> subgraph_input_tensors, constant_tensors, |
| 213 | variable_tensors, output_tensors; |
| 214 | if (subgraph.tensors()) { |
| 215 | for (int i = 0; i < subgraph.tensors()->Length(); ++i) { |
| 216 | const auto* tensor = subgraph.tensors()->Get(i); |
| 217 | if (IsConstantTensor(*tensor, model)) { |
| 218 | constant_tensors.insert(i); |
| 219 | } else if (tensor->is_variable()) { |
| 220 | variable_tensors.insert(i); |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | if (subgraph.inputs()) { |
| 225 | for (const int tensor_idx : *subgraph.inputs()) { |
| 226 | subgraph_input_tensors.insert(tensor_idx); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | if (subgraph.operators()) { |
| 231 | for (int op_idx = 0; op_idx < subgraph.operators()->Length(); ++op_idx) { |
| 232 | const auto* op = subgraph.operators()->Get(op_idx); |
| 233 | if (!model.operator_codes() || |
| 234 | (op->opcode_index() >= model.operator_codes()->size())) { |
| 235 | ReportError(error_reporter, |
| 236 | "Operator %d does not exist in model op codes", |
| 237 | op->opcode_index()); |
| 238 | return false; |
| 239 | } |
| 240 | const auto& opcode = model.operator_codes()->Get(op->opcode_index()); |
| 241 | // Check for invalid inputs by ensuring all exist in produced_tensors. |
| 242 | for (const int input_idx : *op->inputs()) { |
| 243 | if (input_idx == kOptionalTensor) continue; |
| 244 | if (constant_tensors.find(input_idx) == constant_tensors.end() && |
| 245 | variable_tensors.find(input_idx) == variable_tensors.end() && |
| 246 | subgraph_input_tensors.find(input_idx) == |
| 247 | subgraph_input_tensors.end() && |
| 248 | output_tensors.find(input_idx) == output_tensors.end()) { |
| 249 | ReportError(error_reporter, |
| 250 | "Input tensor %d to op %d (%s) is not produced", |
| 251 | input_idx, op_idx, |
| 252 | EnumNameBuiltinOperator(opcode->builtin_code())); |
| 253 | return false; |
| 254 | } |
| 255 | } |
| 256 | // Check for cycles/invalid outputs by ensuring that none exist in |
| 257 | // produced_tensors. |
| 258 | for (const int output_idx : *op->outputs()) { |
| 259 | if (constant_tensors.find(output_idx) != constant_tensors.end()) { |
| 260 | ReportError(error_reporter, |
| 261 | "Output tensor %d to op %d (%s) is a constant", |
| 262 | output_idx, op_idx, |
| 263 | EnumNameBuiltinOperator(opcode->builtin_code())); |
| 264 | return false; |
| 265 | } else if (variable_tensors.find(output_idx) != |
| 266 | variable_tensors.end()) { |
| 267 | ReportError(error_reporter, |
no test coverage detected